Commit dd2d206d by Arve Knudsen Committed by GitHub

Backend: Remove more globals (#29644)

Signed-off-by: Arve Knudsen <arve.knudsen@gmail.com>
parent aa8fb1ae
......@@ -24,8 +24,8 @@ func (hs *HTTPServer) registerRoutes() {
reqCanAccessTeams := middleware.AdminOrFeatureEnabled(hs.Cfg.EditorsCanAdmin)
reqSnapshotPublicModeOrSignedIn := middleware.SnapshotPublicModeOrSignedIn(hs.Cfg)
redirectFromLegacyDashboardURL := middleware.RedirectFromLegacyDashboardURL()
redirectFromLegacyDashboardSoloURL := middleware.RedirectFromLegacyDashboardSoloURL()
redirectFromLegacyPanelEditURL := middleware.RedirectFromLegacyPanelEditURL()
redirectFromLegacyDashboardSoloURL := middleware.RedirectFromLegacyDashboardSoloURL(hs.Cfg)
redirectFromLegacyPanelEditURL := middleware.RedirectFromLegacyPanelEditURL(hs.Cfg)
quota := middleware.Quota(hs.QuotaService)
bind := binding.Bind
......
......@@ -8,6 +8,7 @@ import (
"testing"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/infra/fs"
"github.com/grafana/grafana/pkg/infra/remotecache"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/registry"
......@@ -26,7 +27,7 @@ func loggedInUserScenario(t *testing.T, desc string, url string, fn scenarioFunc
func loggedInUserScenarioWithRole(t *testing.T, desc string, method string, url string, routePattern string, role models.RoleType, fn scenarioFunc) {
t.Run(fmt.Sprintf("%s %s", desc, url), func(t *testing.T) {
defer bus.ClearBusHandlers()
t.Cleanup(bus.ClearBusHandlers)
sc := setupScenarioContext(t, url)
sc.defaultHandler = Wrap(func(c *models.ReqContext) Response {
......@@ -129,6 +130,7 @@ func (sc *scenarioContext) fakeReqNoAssertionsWithCookie(method, url string, coo
type scenarioContext struct {
t *testing.T
cfg *setting.Cfg
m *macaron.Macaron
context *models.ReqContext
resp *httptest.ResponseRecorder
......@@ -146,12 +148,15 @@ func (sc *scenarioContext) exec() {
type scenarioFunc func(c *scenarioContext)
type handlerFunc func(c *models.ReqContext) Response
func getContextHandler(t *testing.T) *contexthandler.ContextHandler {
func getContextHandler(t *testing.T, cfg *setting.Cfg) *contexthandler.ContextHandler {
t.Helper()
if cfg == nil {
cfg = setting.NewCfg()
}
sqlStore := sqlstore.InitTestDB(t)
remoteCacheSvc := &remotecache.RemoteCache{}
cfg := setting.NewCfg()
cfg.RemoteCacheOptions = &setting.RemoteCacheOptions{
Name: "database",
}
......@@ -187,19 +192,24 @@ func getContextHandler(t *testing.T) *contexthandler.ContextHandler {
}
func setupScenarioContext(t *testing.T, url string) *scenarioContext {
cfg := setting.NewCfg()
sc := &scenarioContext{
url: url,
t: t,
cfg: cfg,
}
viewsPath, err := filepath.Abs("../../public/views")
require.NoError(t, err)
exists, err := fs.Exists(viewsPath)
require.NoError(t, err)
require.Truef(t, exists, "Views should be in %q", viewsPath)
sc.m = macaron.New()
sc.m.Use(macaron.Renderer(macaron.RenderOptions{
Directory: viewsPath,
Delims: macaron.Delims{Left: "[[", Right: "]]"},
}))
sc.m.Use(getContextHandler(t).Middleware)
sc.m.Use(getContextHandler(t, cfg).Middleware)
return sc
}
......
......@@ -15,6 +15,7 @@ import (
"github.com/grafana/grafana/pkg/services/dashboards"
"github.com/grafana/grafana/pkg/services/live"
"github.com/grafana/grafana/pkg/services/provisioning"
"github.com/grafana/grafana/pkg/services/quota"
"github.com/grafana/grafana/pkg/setting"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
......@@ -1177,11 +1178,15 @@ func postDashboardScenario(t *testing.T, desc string, url string, routePattern s
t.Run(fmt.Sprintf("%s %s", desc, url), func(t *testing.T) {
t.Cleanup(bus.ClearBusHandlers)
cfg := setting.NewCfg()
hs := HTTPServer{
Bus: bus.GetBus(),
Cfg: setting.NewCfg(),
Cfg: cfg,
ProvisioningService: provisioning.NewProvisioningServiceMock(),
Live: &live.GrafanaLive{Cfg: setting.NewCfg()},
QuotaService: &quota.QuotaService{
Cfg: cfg,
},
}
sc := setupScenarioContext(t, url)
......@@ -1238,11 +1243,13 @@ func restoreDashboardVersionScenario(t *testing.T, desc string, url string, rout
t.Run(fmt.Sprintf("%s %s", desc, url), func(t *testing.T) {
defer bus.ClearBusHandlers()
cfg := setting.NewCfg()
hs := HTTPServer{
Cfg: setting.NewCfg(),
Cfg: cfg,
Bus: bus.GetBus(),
ProvisioningService: provisioning.NewProvisioningServiceMock(),
Live: &live.GrafanaLive{Cfg: setting.NewCfg()},
Live: &live.GrafanaLive{Cfg: cfg},
QuotaService: &quota.QuotaService{Cfg: cfg},
}
sc := setupScenarioContext(t, url)
......
......@@ -52,7 +52,7 @@ func setupTestEnvironment(t *testing.T, cfg *setting.Cfg) (*macaron.Macaron, *HT
}
m := macaron.New()
m.Use(getContextHandler(t).Middleware)
m.Use(getContextHandler(t, cfg).Middleware)
m.Use(macaron.Renderer(macaron.RenderOptions{
Directory: filepath.Join(setting.StaticRootPath, "views"),
IndentJSON: true,
......
......@@ -337,11 +337,11 @@ func (hs *HTTPServer) addMiddlewaresAndStaticRoutes() {
m.Use(hs.metricsEndpoint)
m.Use(hs.ContextHandler.Middleware)
m.Use(middleware.OrgRedirect())
m.Use(middleware.OrgRedirect(hs.Cfg))
// needs to be after context handler
if setting.EnforceDomain {
m.Use(middleware.ValidateHostHeader(hs.Cfg.Domain))
m.Use(middleware.ValidateHostHeader(hs.Cfg))
}
m.Use(middleware.HandleNoCacheHeader())
......
......@@ -458,7 +458,7 @@ func (hs *HTTPServer) setIndexViewData(c *models.ReqContext) (*dtos.IndexViewDat
func (hs *HTTPServer) Index(c *models.ReqContext) {
data, err := hs.setIndexViewData(c)
if err != nil {
c.Handle(500, "Failed to get settings", err)
c.Handle(hs.Cfg, 500, "Failed to get settings", err)
return
}
c.HTML(200, "index", data)
......@@ -472,7 +472,7 @@ func (hs *HTTPServer) NotFoundHandler(c *models.ReqContext) {
data, err := hs.setIndexViewData(c)
if err != nil {
c.Handle(500, "Failed to get settings", err)
c.Handle(hs.Cfg, 500, "Failed to get settings", err)
return
}
......
......@@ -11,7 +11,6 @@ import (
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/ldap"
"github.com/grafana/grafana/pkg/services/multildap"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util"
)
......@@ -187,7 +186,7 @@ func (hs *HTTPServer) PostSyncUserWithLDAP(c *models.ReqContext) Response {
user, _, err := ldapServer.User(query.Result.Login)
if err != nil {
if errors.Is(err, multildap.ErrDidNotFindUser) { // User was not in the LDAP server - we need to take action:
if setting.AdminUser == query.Result.Login { // User is *the* Grafana Admin. We cannot disable it.
if hs.Cfg.AdminUser == query.Result.Login { // User is *the* Grafana Admin. We cannot disable it.
errMsg := fmt.Sprintf(`Refusing to sync grafana super admin "%s" - it would be disabled`, query.Result.Login)
ldapLogger.Error(errMsg)
return Error(http.StatusBadRequest, errMsg, err)
......
......@@ -375,7 +375,7 @@ func TestGetLDAPStatusAPIEndpoint(t *testing.T) {
// PostSyncUserWithLDAP tests
// ***
func postSyncUserWithLDAPContext(t *testing.T, requestURL string, preHook func(t *testing.T)) *scenarioContext {
func postSyncUserWithLDAPContext(t *testing.T, requestURL string, preHook func(*testing.T, *scenarioContext)) *scenarioContext {
t.Helper()
sc := setupScenarioContext(t, requestURL)
......@@ -387,7 +387,7 @@ func postSyncUserWithLDAPContext(t *testing.T, requestURL string, preHook func(t
setting.LDAPEnabled = true
hs := &HTTPServer{
Cfg: setting.NewCfg(),
Cfg: sc.cfg,
AuthTokenService: auth.NewFakeUserAuthTokenService(),
}
......@@ -402,7 +402,7 @@ func postSyncUserWithLDAPContext(t *testing.T, requestURL string, preHook func(t
req, err := http.NewRequest(http.MethodPost, requestURL, nil)
require.NoError(t, err)
preHook(t)
preHook(t, sc)
sc.req = req
sc.exec()
......@@ -411,7 +411,7 @@ func postSyncUserWithLDAPContext(t *testing.T, requestURL string, preHook func(t
}
func TestPostSyncUserWithLDAPAPIEndpoint_Success(t *testing.T) {
sc := postSyncUserWithLDAPContext(t, "/api/admin/ldap/sync/34", func(t *testing.T) {
sc := postSyncUserWithLDAPContext(t, "/api/admin/ldap/sync/34", func(t *testing.T, sc *scenarioContext) {
getLDAPConfig = func(*setting.Cfg) (*ldap.Config, error) {
return &ldap.Config{}, nil
}
......@@ -456,7 +456,7 @@ func TestPostSyncUserWithLDAPAPIEndpoint_Success(t *testing.T) {
}
func TestPostSyncUserWithLDAPAPIEndpoint_WhenUserNotFound(t *testing.T) {
sc := postSyncUserWithLDAPContext(t, "/api/admin/ldap/sync/34", func(t *testing.T) {
sc := postSyncUserWithLDAPContext(t, "/api/admin/ldap/sync/34", func(t *testing.T, sc *scenarioContext) {
getLDAPConfig = func(*setting.Cfg) (*ldap.Config, error) {
return &ldap.Config{}, nil
}
......@@ -484,7 +484,7 @@ func TestPostSyncUserWithLDAPAPIEndpoint_WhenUserNotFound(t *testing.T) {
}
func TestPostSyncUserWithLDAPAPIEndpoint_WhenGrafanaAdmin(t *testing.T) {
sc := postSyncUserWithLDAPContext(t, "/api/admin/ldap/sync/34", func(t *testing.T) {
sc := postSyncUserWithLDAPContext(t, "/api/admin/ldap/sync/34", func(t *testing.T, sc *scenarioContext) {
getLDAPConfig = func(*setting.Cfg) (*ldap.Config, error) {
return &ldap.Config{}, nil
}
......@@ -495,9 +495,7 @@ func TestPostSyncUserWithLDAPAPIEndpoint_WhenGrafanaAdmin(t *testing.T) {
userSearchError = multildap.ErrDidNotFindUser
admin := setting.AdminUser
t.Cleanup(func() { setting.AdminUser = admin })
setting.AdminUser = "ldap-daniel"
sc.cfg.AdminUser = "ldap-daniel"
bus.AddHandler("test", func(q *models.GetUserByIdQuery) error {
require.Equal(t, q.Id, int64(34))
......@@ -527,7 +525,7 @@ func TestPostSyncUserWithLDAPAPIEndpoint_WhenGrafanaAdmin(t *testing.T) {
}
func TestPostSyncUserWithLDAPAPIEndpoint_WhenUserNotInLDAP(t *testing.T) {
sc := postSyncUserWithLDAPContext(t, "/api/admin/ldap/sync/34", func(t *testing.T) {
sc := postSyncUserWithLDAPContext(t, "/api/admin/ldap/sync/34", func(t *testing.T, sc *scenarioContext) {
getLDAPConfig = func(*setting.Cfg) (*ldap.Config, error) {
return &ldap.Config{}, nil
}
......
......@@ -77,7 +77,7 @@ func (hs *HTTPServer) CookieOptionsFromCfg() cookies.CookieOptions {
func (hs *HTTPServer) LoginView(c *models.ReqContext) {
viewData, err := setIndexViewData(hs, c)
if err != nil {
c.Handle(500, "Failed to get settings", err)
c.Handle(hs.Cfg, 500, "Failed to get settings", err)
return
}
......@@ -117,7 +117,7 @@ func (hs *HTTPServer) LoginView(c *models.ReqContext) {
user := &models.User{Id: c.SignedInUser.UserId, Email: c.SignedInUser.Email, Login: c.SignedInUser.Login}
err := hs.loginUserWithUser(user, c)
if err != nil {
c.Handle(500, "Failed to sign in user", err)
c.Handle(hs.Cfg, 500, "Failed to sign in user", err)
return
}
}
......
......@@ -277,7 +277,7 @@ type LoginError struct {
}
func (hs *HTTPServer) handleOAuthLoginError(ctx *models.ReqContext, info models.LoginInfo, err LoginError) {
ctx.Handle(err.HttpStatus, err.PublicMessage, err.Err)
ctx.Handle(hs.Cfg, err.HttpStatus, err.PublicMessage, err.Err)
info.Error = err.Err
if info.Error == nil {
......
......@@ -25,7 +25,11 @@ import (
"github.com/stretchr/testify/require"
)
func mockSetIndexViewData() {
func fakeSetIndexViewData(t *testing.T) {
origSetIndexViewData := setIndexViewData
t.Cleanup(func() {
setIndexViewData = origSetIndexViewData
})
setIndexViewData = func(*HTTPServer, *models.ReqContext) (*dtos.IndexViewData, error) {
data := &dtos.IndexViewData{
User: &dtos.CurrentUser{},
......@@ -36,22 +40,16 @@ func mockSetIndexViewData() {
}
}
func resetSetIndexViewData() {
setIndexViewData = (*HTTPServer).setIndexViewData
}
func mockViewIndex() {
func fakeViewIndex(t *testing.T) {
origGetViewIndex := getViewIndex
t.Cleanup(func() {
getViewIndex = origGetViewIndex
})
getViewIndex = func() string {
return "index-template"
}
}
func resetViewIndex() {
getViewIndex = func() string {
return ViewIndex
}
}
func getBody(resp *httptest.ResponseRecorder) (string, error) {
responseData, err := ioutil.ReadAll(resp.Body)
if err != nil {
......@@ -86,16 +84,15 @@ type redirectCase struct {
redirectURL string
}
func TestLoginErrorCookieApiEndpoint(t *testing.T) {
mockSetIndexViewData()
defer resetSetIndexViewData()
func TestLoginErrorCookieAPIEndpoint(t *testing.T) {
fakeSetIndexViewData(t)
mockViewIndex()
defer resetViewIndex()
fakeViewIndex(t)
sc := setupScenarioContext(t, "/login")
cfg := setting.NewCfg()
hs := &HTTPServer{
Cfg: setting.NewCfg(),
Cfg: cfg,
License: &licensing.OSSLicensingService{},
}
......@@ -103,7 +100,7 @@ func TestLoginErrorCookieApiEndpoint(t *testing.T) {
hs.LoginView(c)
})
setting.LoginCookieName = "grafana_session"
cfg.LoginCookieName = "grafana_session"
setting.SecretKey = "login_testing"
setting.OAuthService = &setting.OAuther{}
......@@ -142,11 +139,9 @@ func TestLoginErrorCookieApiEndpoint(t *testing.T) {
}
func TestLoginViewRedirect(t *testing.T) {
mockSetIndexViewData()
defer resetSetIndexViewData()
fakeSetIndexViewData(t)
mockViewIndex()
defer resetViewIndex()
fakeViewIndex(t)
sc := setupScenarioContext(t, "/login")
hs := &HTTPServer{
Cfg: setting.NewCfg(),
......@@ -318,11 +313,9 @@ func TestLoginViewRedirect(t *testing.T) {
}
func TestLoginPostRedirect(t *testing.T) {
mockSetIndexViewData()
defer resetSetIndexViewData()
fakeSetIndexViewData(t)
mockViewIndex()
defer resetViewIndex()
fakeViewIndex(t)
sc := setupScenarioContext(t, "/login")
hs := &HTTPServer{
log: &FakeLogger{},
......@@ -478,8 +471,7 @@ func TestLoginPostRedirect(t *testing.T) {
}
func TestLoginOAuthRedirect(t *testing.T) {
mockSetIndexViewData()
defer resetSetIndexViewData()
fakeSetIndexViewData(t)
sc := setupScenarioContext(t, "/login")
hs := &HTTPServer{
......@@ -511,11 +503,9 @@ func TestLoginOAuthRedirect(t *testing.T) {
}
func TestLoginInternal(t *testing.T) {
mockSetIndexViewData()
defer resetSetIndexViewData()
fakeSetIndexViewData(t)
mockViewIndex()
defer resetViewIndex()
fakeViewIndex(t)
sc := setupScenarioContext(t, "/login")
hs := &HTTPServer{
Cfg: setting.NewCfg(),
......@@ -559,24 +549,23 @@ func TestAuthProxyLoginEnableLoginTokenDisabled(t *testing.T) {
func TestAuthProxyLoginWithEnableLoginToken(t *testing.T) {
sc := setupAuthProxyLoginTest(t, true)
require.Equal(t, sc.resp.Code, 302)
assert.Equal(t, sc.resp.Code, 302)
location, ok := sc.resp.Header()["Location"]
assert.True(t, ok)
assert.Equal(t, location[0], "/")
setCookie, ok := sc.resp.Header()["Set-Cookie"]
assert.True(t, ok, "Set-Cookie exists")
setCookie := sc.resp.Header()["Set-Cookie"]
require.NotNil(t, setCookie, "Set-Cookie should exist")
assert.Equal(t, "grafana_session=; Path=/; Max-Age=0; HttpOnly", setCookie[0])
}
func setupAuthProxyLoginTest(t *testing.T, enableLoginToken bool) *scenarioContext {
mockSetIndexViewData()
defer resetSetIndexViewData()
fakeSetIndexViewData(t)
sc := setupScenarioContext(t, "/login")
sc.cfg.LoginCookieName = "grafana_session"
hs := &HTTPServer{
Cfg: setting.NewCfg(),
Cfg: sc.cfg,
License: &licensing.OSSLicensingService{},
AuthTokenService: auth.NewFakeUserAuthTokenService(),
log: log.New("hello"),
......@@ -592,8 +581,8 @@ func setupAuthProxyLoginTest(t *testing.T, enableLoginToken bool) *scenarioConte
setting.OAuthService = &setting.OAuther{}
setting.OAuthService.OAuthInfos = make(map[string]*setting.OAuthInfo)
hs.Cfg.AuthProxyEnabled = true
hs.Cfg.AuthProxyEnableLoginToken = enableLoginToken
sc.cfg.AuthProxyEnabled = true
sc.cfg.AuthProxyEnableLoginToken = enableLoginToken
sc.m.Get(sc.url, sc.defaultHandler)
sc.fakeReqNoAssertions("GET", sc.url).exec()
......
......@@ -17,7 +17,7 @@ import (
func (hs *HTTPServer) RenderToPng(c *models.ReqContext) {
queryReader, err := util.NewURLQueryReader(c.Req.URL)
if err != nil {
c.Handle(400, "Render parameters error", err)
c.Handle(hs.Cfg, 400, "Render parameters error", err)
return
}
......@@ -25,25 +25,25 @@ func (hs *HTTPServer) RenderToPng(c *models.ReqContext) {
width, err := strconv.Atoi(queryReader.Get("width", "800"))
if err != nil {
c.Handle(400, "Render parameters error", fmt.Errorf("cannot parse width as int: %s", err))
c.Handle(hs.Cfg, 400, "Render parameters error", fmt.Errorf("cannot parse width as int: %s", err))
return
}
height, err := strconv.Atoi(queryReader.Get("height", "400"))
if err != nil {
c.Handle(400, "Render parameters error", fmt.Errorf("cannot parse height as int: %s", err))
c.Handle(hs.Cfg, 400, "Render parameters error", fmt.Errorf("cannot parse height as int: %s", err))
return
}
timeout, err := strconv.Atoi(queryReader.Get("timeout", "60"))
if err != nil {
c.Handle(400, "Render parameters error", fmt.Errorf("cannot parse timeout as int: %s", err))
c.Handle(hs.Cfg, 400, "Render parameters error", fmt.Errorf("cannot parse timeout as int: %s", err))
return
}
scale, err := strconv.ParseFloat(queryReader.Get("scale", "1"), 64)
if err != nil {
c.Handle(400, "Render parameters error", fmt.Errorf("cannot parse scale as float: %s", err))
c.Handle(hs.Cfg, 400, "Render parameters error", fmt.Errorf("cannot parse scale as float: %s", err))
return
}
......@@ -69,19 +69,19 @@ func (hs *HTTPServer) RenderToPng(c *models.ReqContext) {
})
if err != nil {
if errors.Is(err, rendering.ErrTimeout) {
c.Handle(500, err.Error(), err)
c.Handle(hs.Cfg, 500, err.Error(), err)
return
}
if errors.Is(err, rendering.ErrPhantomJSNotInstalled) {
if strings.HasPrefix(runtime.GOARCH, "arm") {
c.Handle(500, "Rendering failed - PhantomJS isn't included in arm build per default", err)
c.Handle(hs.Cfg, 500, "Rendering failed - PhantomJS isn't included in arm build per default", err)
} else {
c.Handle(500, "Rendering failed - PhantomJS isn't installed correctly", err)
c.Handle(hs.Cfg, 500, "Rendering failed - PhantomJS isn't installed correctly", err)
}
return
}
c.Handle(500, "Rendering failed.", err)
c.Handle(hs.Cfg, 500, "Rendering failed.", err)
return
}
......
......@@ -67,5 +67,5 @@ func WriteSessionCookie(ctx *models.ReqContext, cfg *setting.Cfg, value string,
maxAge = int(maxLifetime.Seconds())
}
WriteCookie(ctx.Resp, setting.LoginCookieName, url.QueryEscape(value), maxAge, nil)
WriteCookie(ctx.Resp, cfg.LoginCookieName, url.QueryEscape(value), maxAge, nil)
}
......@@ -7,12 +7,11 @@ import (
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/setting"
"gopkg.in/macaron.v1"
)
func getDashboardURLBySlug(orgID int64, slug string) (string, error) {
// TODO: Drop bus call
query := models.GetDashboardQuery{Slug: slug, OrgId: orgID}
if err := bus.Dispatch(&query); err != nil {
return "", models.ErrDashboardNotFound
}
......@@ -20,7 +19,7 @@ func getDashboardURLBySlug(orgID int64, slug string) (string, error) {
return models.GetDashboardUrl(query.Result.Uid, query.Result.Slug), nil
}
func RedirectFromLegacyDashboardURL() macaron.Handler {
func RedirectFromLegacyDashboardURL() func(c *models.ReqContext) {
return func(c *models.ReqContext) {
slug := c.Params("slug")
......@@ -36,47 +35,50 @@ func RedirectFromLegacyDashboardURL() macaron.Handler {
// In Grafana v7.0 we changed panel edit & view query parameters.
// This middleware tries to detect those old url parameters and direct to the new url query params
func RedirectFromLegacyPanelEditURL() macaron.Handler {
func RedirectFromLegacyPanelEditURL(cfg *setting.Cfg) func(c *models.ReqContext) {
return func(c *models.ReqContext) {
queryParams := c.Req.URL.Query()
panelId, hasPanelId := queryParams["panelId"]
panelID, hasPanelID := queryParams["panelId"]
_, hasFullscreen := queryParams["fullscreen"]
_, hasEdit := queryParams["edit"]
if hasPanelId && hasFullscreen {
if hasPanelID && hasFullscreen {
delete(queryParams, "panelId")
delete(queryParams, "fullscreen")
delete(queryParams, "edit")
if hasEdit {
queryParams["editPanel"] = panelId
queryParams["editPanel"] = panelID
} else {
queryParams["viewPanel"] = panelId
queryParams["viewPanel"] = panelID
}
newURL := setting.ToAbsUrl(fmt.Sprintf("%s?%s", strings.TrimPrefix(c.Req.URL.Path, "/"), queryParams.Encode()))
newURL := fmt.Sprintf("%s%s?%s", cfg.AppURL, strings.TrimPrefix(c.Req.URL.Path, "/"), queryParams.Encode())
c.Redirect(newURL, 301)
}
}
}
func RedirectFromLegacyDashboardSoloURL() macaron.Handler {
func RedirectFromLegacyDashboardSoloURL(cfg *setting.Cfg) func(c *models.ReqContext) {
return func(c *models.ReqContext) {
slug := c.Params("slug")
renderRequest := c.QueryBool("render")
if slug != "" {
if url, err := getDashboardURLBySlug(c.OrgId, slug); err == nil {
if renderRequest && strings.Contains(url, setting.AppSubUrl) {
url = strings.Replace(url, setting.AppSubUrl, "", 1)
}
url = strings.Replace(url, "/d/", "/d-solo/", 1)
url = fmt.Sprintf("%s?%s", url, c.Req.URL.RawQuery)
c.Redirect(url, 301)
url, err := getDashboardURLBySlug(c.OrgId, slug)
if err != nil {
return
}
if renderRequest && strings.Contains(url, cfg.AppSubURL) {
url = strings.Replace(url, cfg.AppSubURL, "", 1)
}
url = strings.Replace(url, "/d/", "/d-solo/", 1)
url = fmt.Sprintf("%s?%s", url, c.Req.URL.RawQuery)
c.Redirect(url, 301)
return
}
}
}
......@@ -7,74 +7,79 @@ import (
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/util"
. "github.com/smartystreets/goconvey/convey"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestMiddlewareDashboardRedirect(t *testing.T) {
Convey("Given the dashboard redirect middleware", t, func() {
bus.ClearBusHandlers()
redirectFromLegacyDashboardUrl := RedirectFromLegacyDashboardURL()
redirectFromLegacyDashboardSoloUrl := RedirectFromLegacyDashboardSoloURL()
bus.ClearBusHandlers()
fakeDash := models.NewDashboard("Child dash")
fakeDash.Id = 1
fakeDash.FolderId = 1
fakeDash.HasAcl = false
fakeDash.Uid = util.GenerateShortUID()
fakeDash := models.NewDashboard("Child dash")
fakeDash.Id = 1
fakeDash.FolderId = 1
fakeDash.HasAcl = false
fakeDash.Uid = util.GenerateShortUID()
middlewareScenario(t, "GET dashboard by legacy url", func(t *testing.T, sc *scenarioContext) {
redirectFromLegacyDashboardURL := RedirectFromLegacyDashboardURL()
middlewareScenario(t, "GET dashboard by legacy url", func(t *testing.T, sc *scenarioContext) {
bus.AddHandler("test", func(query *models.GetDashboardQuery) error {
query.Result = fakeDash
return nil
})
sc.m.Get("/dashboard/db/:slug", redirectFromLegacyDashboardUrl, sc.defaultHandler)
bus.AddHandler("test", func(query *models.GetDashboardQuery) error {
t.Log("Returning fake dashboard")
query.Result = fakeDash
return nil
})
sc.fakeReqWithParams("GET", "/dashboard/db/dash?orgId=1&panelId=2", map[string]string{}).exec()
sc.handlerFunc = redirectFromLegacyDashboardURL
sc.m.Get("/dashboard/db/:slug", sc.defaultHandler)
sc.fakeReqWithParams("GET", "/dashboard/db/dash?orgId=1&panelId=2", map[string]string{}).exec()
assert.Equal(t, 301, sc.resp.Code)
// nolint:bodyclose
resp := sc.resp.Result()
t.Cleanup(func() {
err := resp.Body.Close()
assert.NoError(t, err)
})
redirectURL, err := resp.Location()
require.NoError(t, err)
assert.Equal(t, models.GetDashboardUrl(fakeDash.Uid, fakeDash.Slug), redirectURL.Path)
assert.Equal(t, 2, len(redirectURL.Query()))
assert.Equal(t, 301, sc.resp.Code)
// nolint:bodyclose
resp := sc.resp.Result()
t.Cleanup(func() {
err := resp.Body.Close()
assert.NoError(t, err)
})
redirectURL, err := resp.Location()
require.NoError(t, err)
assert.Equal(t, models.GetDashboardUrl(fakeDash.Uid, fakeDash.Slug), redirectURL.Path)
assert.Len(t, redirectURL.Query(), 2)
})
middlewareScenario(t, "GET dashboard solo by legacy url", func(t *testing.T, sc *scenarioContext) {
bus.AddHandler("test", func(query *models.GetDashboardQuery) error {
query.Result = fakeDash
return nil
})
middlewareScenario(t, "GET dashboard solo by legacy url", func(t *testing.T, sc *scenarioContext) {
redirectFromLegacyDashboardSoloURL := RedirectFromLegacyDashboardSoloURL(sc.cfg)
sc.m.Get("/dashboard-solo/db/:slug", redirectFromLegacyDashboardSoloUrl, sc.defaultHandler)
bus.AddHandler("test", func(query *models.GetDashboardQuery) error {
t.Log("Returning fake dashboard")
query.Result = fakeDash
return nil
})
sc.handlerFunc = redirectFromLegacyDashboardSoloURL
sc.m.Get("/dashboard-solo/db/:slug", 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()
assert.Equal(t, 301, sc.resp.Code)
// nolint:bodyclose
resp := sc.resp.Result()
t.Cleanup(func() {
err := resp.Body.Close()
assert.NoError(t, err)
})
redirectURL, err := resp.Location()
require.NoError(t, err)
expectedURL := models.GetDashboardUrl(fakeDash.Uid, fakeDash.Slug)
expectedURL = strings.Replace(expectedURL, "/d/", "/d-solo/", 1)
assert.Equal(t, expectedURL, redirectURL.Path)
assert.Equal(t, 2, len(redirectURL.Query()))
require.Equal(t, 301, sc.resp.Code)
// nolint:bodyclose
resp := sc.resp.Result()
t.Cleanup(func() {
err := resp.Body.Close()
assert.NoError(t, err)
})
redirectURL, err := resp.Location()
require.NoError(t, err)
// XXX: Should this be called path??
expectedURL := models.GetDashboardUrl(fakeDash.Uid, fakeDash.Slug)
expectedURL = strings.Replace(expectedURL, "/d/", "/d-solo/", 1)
assert.Equal(t, expectedURL, redirectURL.Path)
assert.Len(t, redirectURL.Query(), 2)
})
}
middlewareScenario(t, "GET dashboard by legacy edit url", func(t *testing.T, sc *scenarioContext) {
sc.m.Get("/d/:uid/:slug", RedirectFromLegacyPanelEditURL(), sc.defaultHandler)
func TestMiddlewareDashboardRedirect_legacyEditPanel(t *testing.T) {
middlewareScenario(t, "GET dashboard by legacy edit URL", func(t *testing.T, sc *scenarioContext) {
sc.handlerFunc = RedirectFromLegacyPanelEditURL(sc.cfg)
sc.m.Get("/d/:uid/:slug", sc.defaultHandler)
sc.fakeReqWithParams("GET", "/d/asd/dash?orgId=1&panelId=12&fullscreen&edit", map[string]string{}).exec()
......
......@@ -17,6 +17,7 @@ import (
"github.com/grafana/grafana/pkg/api/dtos"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/components/gtime"
"github.com/grafana/grafana/pkg/infra/fs"
"github.com/grafana/grafana/pkg/infra/remotecache"
"github.com/grafana/grafana/pkg/login"
"github.com/grafana/grafana/pkg/models"
......@@ -30,8 +31,6 @@ import (
"github.com/grafana/grafana/pkg/util"
)
const errorTemplate = "error-template"
func fakeGetTime() func() time.Time {
var timeSeed int64
return func() time.Time {
......@@ -42,12 +41,6 @@ func fakeGetTime() func() time.Time {
}
func TestMiddleWareSecurityHeaders(t *testing.T) {
origErrTemplateName := setting.ErrTemplateName
t.Cleanup(func() {
setting.ErrTemplateName = origErrTemplateName
})
setting.ErrTemplateName = errorTemplate
middlewareScenario(t, "middleware should get correct x-xss-protection header", func(t *testing.T, sc *scenarioContext) {
sc.fakeReq("GET", "/api/").exec()
assert.Equal(t, "1; mode=block", sc.resp.Header().Get("X-XSS-Protection"))
......@@ -79,11 +72,7 @@ func TestMiddleWareSecurityHeaders(t *testing.T) {
}
func TestMiddlewareContext(t *testing.T) {
origErrTemplateName := setting.ErrTemplateName
t.Cleanup(func() {
setting.ErrTemplateName = origErrTemplateName
})
setting.ErrTemplateName = errorTemplate
const noCache = "no-cache"
middlewareScenario(t, "middleware should add context to injector", func(t *testing.T, sc *scenarioContext) {
sc.fakeReq("GET", "/").exec()
......@@ -97,8 +86,8 @@ func TestMiddlewareContext(t *testing.T) {
middlewareScenario(t, "middleware should add Cache-Control header for requests to API", func(t *testing.T, sc *scenarioContext) {
sc.fakeReq("GET", "/api/search").exec()
assert.Equal(t, "no-cache", sc.resp.Header().Get("Cache-Control"))
assert.Equal(t, "no-cache", sc.resp.Header().Get("Pragma"))
assert.Equal(t, noCache, sc.resp.Header().Get("Cache-Control"))
assert.Equal(t, noCache, sc.resp.Header().Get("Pragma"))
assert.Equal(t, "-1", sc.resp.Header().Get("Expires"))
})
......@@ -110,20 +99,23 @@ func TestMiddlewareContext(t *testing.T) {
assert.Empty(t, sc.resp.Header().Get("Expires"))
})
middlewareScenario(t, "middleware should add Cache-Control header for requests with html response", func(
middlewareScenario(t, "middleware should add Cache-Control header for requests with HTML response", func(
t *testing.T, sc *scenarioContext) {
sc.handler(func(c *models.ReqContext) {
sc.handlerFunc = func(c *models.ReqContext) {
t.Log("Handler called")
data := &dtos.IndexViewData{
User: &dtos.CurrentUser{},
Settings: map[string]interface{}{},
NavTree: []*dtos.NavLink{},
}
t.Log("Calling HTML", "data", data, "render", c.Render)
c.HTML(200, "index-template", data)
})
t.Log("Returned HTML with code 200")
}
sc.fakeReq("GET", "/").exec()
assert.Equal(t, 200, sc.resp.Code)
assert.Equal(t, "no-cache", sc.resp.Header().Get("Cache-Control"))
assert.Equal(t, "no-cache", sc.resp.Header().Get("Pragma"))
require.Equal(t, 200, sc.resp.Code)
assert.Equal(t, noCache, sc.resp.Header().Get("Cache-Control"))
assert.Equal(t, noCache, sc.resp.Header().Get("Pragma"))
assert.Equal(t, "-1", sc.resp.Header().Get("Expires"))
})
......@@ -150,7 +142,7 @@ func TestMiddlewareContext(t *testing.T) {
assert.Equal(t, contexthandler.InvalidAPIKey, sc.respJson["message"])
})
middlewareScenario(t, "Valid api key", func(t *testing.T, sc *scenarioContext) {
middlewareScenario(t, "Valid API key", func(t *testing.T, sc *scenarioContext) {
const orgID int64 = 12
keyhash, err := util.EncodePassword("v5nAwpMafFP6znaS4urhdWDLS5511M42", "asd")
require.NoError(t, err)
......@@ -162,15 +154,15 @@ func TestMiddlewareContext(t *testing.T) {
sc.fakeReq("GET", "/").withValidApiKey().exec()
assert.Equal(t, 200, sc.resp.Code)
require.Equal(t, 200, sc.resp.Code)
assert.True(t, sc.context.IsSignedIn)
assert.Equal(t, orgID, sc.context.OrgId)
assert.Equal(t, models.ROLE_EDITOR, sc.context.OrgRole)
})
middlewareScenario(t, "Valid api key, but does not match db hash", func(t *testing.T, sc *scenarioContext) {
keyhash := "Something_not_matching"
middlewareScenario(t, "Valid API key, but does not match DB hash", func(t *testing.T, sc *scenarioContext) {
const keyhash = "Something_not_matching"
bus.AddHandler("test", func(query *models.GetApiKeyByNameQuery) error {
query.Result = &models.ApiKey{OrgId: 12, Role: models.ROLE_EDITOR, Key: keyhash}
......@@ -223,14 +215,16 @@ func TestMiddlewareContext(t *testing.T) {
sc.fakeReq("GET", "/").exec()
require.NotNil(t, sc.context)
require.NotNil(t, sc.context.UserToken)
assert.True(t, sc.context.IsSignedIn)
assert.Equal(t, userID, sc.context.UserId)
assert.Equal(t, userID, sc.context.UserToken.UserId)
assert.Equal(t, "token", sc.context.UserToken.UnhashedToken)
assert.Equal(t, "", sc.resp.Header().Get("Set-Cookie"))
assert.Empty(t, sc.resp.Header().Get("Set-Cookie"))
})
middlewareScenario(t, "Non-expired auth token in cookie which are being rotated", func(t *testing.T, sc *scenarioContext) {
middlewareScenario(t, "Non-expired auth token in cookie which is being rotated", func(t *testing.T, sc *scenarioContext) {
const userID int64 = 12
sc.withTokenSessionCookie("token")
......@@ -253,7 +247,7 @@ func TestMiddlewareContext(t *testing.T) {
return true, nil
}
maxAge := int(setting.LoginMaxLifetime.Seconds())
maxAge := int(sc.cfg.LoginMaxLifetime.Seconds())
sameSiteModes := []http.SameSite{
http.SameSiteNoneMode,
......@@ -269,11 +263,11 @@ func TestMiddlewareContext(t *testing.T) {
setting.CookieSameSiteMode = sameSiteMode
expectedCookiePath := "/"
if len(setting.AppSubUrl) > 0 {
expectedCookiePath = setting.AppSubUrl
if len(sc.cfg.AppSubURL) > 0 {
expectedCookiePath = sc.cfg.AppSubURL
}
expectedCookie := &http.Cookie{
Name: setting.LoginCookieName,
Name: sc.cfg.LoginCookieName,
Value: "rotated",
Path: expectedCookiePath,
HttpOnly: true,
......@@ -303,11 +297,11 @@ func TestMiddlewareContext(t *testing.T) {
setting.CookieSameSiteMode = http.SameSiteLaxMode
expectedCookiePath := "/"
if len(setting.AppSubUrl) > 0 {
expectedCookiePath = setting.AppSubUrl
if len(sc.cfg.AppSubURL) > 0 {
expectedCookiePath = sc.cfg.AppSubURL
}
expectedCookie := &http.Cookie{
Name: setting.LoginCookieName,
Name: sc.cfg.LoginCookieName,
Value: "rotated",
Path: expectedCookiePath,
HttpOnly: true,
......@@ -556,6 +550,8 @@ func middlewareScenario(t *testing.T, desc string, fn scenarioFunc, cbs ...func(
cfg := setting.NewCfg()
cfg.LoginCookieName = "grafana_session"
cfg.LoginMaxLifetime = loginMaxLifetime
// Required when rendering errors
cfg.ErrTemplateName = "error-template"
for _, cb := range cbs {
cb(cfg)
}
......@@ -564,6 +560,9 @@ func middlewareScenario(t *testing.T, desc string, fn scenarioFunc, cbs ...func(
viewsPath, err := filepath.Abs("../../public/views")
require.NoError(t, err)
exists, err := fs.Exists(viewsPath)
require.NoError(t, err)
require.Truef(t, exists, "Views directory should exist at %q", viewsPath)
sc.m = macaron.New()
sc.m.Use(AddDefaultResponseHeaders(cfg))
......@@ -575,7 +574,7 @@ func middlewareScenario(t *testing.T, desc string, fn scenarioFunc, cbs ...func(
ctxHdlr := getContextHandler(t, cfg)
sc.contextHandler = ctxHdlr
sc.m.Use(ctxHdlr.Middleware)
sc.m.Use(OrgRedirect())
sc.m.Use(OrgRedirect(sc.cfg))
sc.userAuthTokenService = ctxHdlr.AuthTokenService.(*auth.FakeUserAuthTokenService)
sc.remoteCacheService = ctxHdlr.RemoteCache
......@@ -587,6 +586,7 @@ func middlewareScenario(t *testing.T, desc string, fn scenarioFunc, cbs ...func(
if sc.handlerFunc != nil {
sc.handlerFunc(sc.context)
} else {
t.Log("Returning JSON OK")
resp := make(map[string]interface{})
resp["message"] = "OK"
c.JSON(200, resp)
......
......@@ -14,7 +14,7 @@ import (
// OrgRedirect changes org and redirects users if the
// querystring `orgId` doesn't match the active org.
func OrgRedirect() macaron.Handler {
func OrgRedirect(cfg *setting.Cfg) macaron.Handler {
return func(res http.ResponseWriter, req *http.Request, c *macaron.Context) {
orgIdValue := req.URL.Query().Get("orgId")
orgId, err := strconv.ParseInt(orgIdValue, 10, 64)
......@@ -43,7 +43,7 @@ func OrgRedirect() macaron.Handler {
return
}
newURL := setting.ToAbsUrl(fmt.Sprintf("%s?%s", strings.TrimPrefix(c.Req.URL.Path, "/"), c.Req.URL.Query().Encode()))
newURL := fmt.Sprintf("%s%s?%s", cfg.AppURL, strings.TrimPrefix(c.Req.URL.Path, "/"), c.Req.URL.Query().Encode())
c.Redirect(newURL, 302)
}
}
......@@ -10,13 +10,13 @@ import (
)
// Quota returns a function that returns a function used to call quotaservice based on target name
func Quota(quotaService *quota.QuotaService) func(target string) macaron.Handler {
func Quota(quotaService *quota.QuotaService) func(string) macaron.Handler {
//https://open.spotify.com/track/7bZSoBEAEEUsGEuLOf94Jm?si=T1Tdju5qRSmmR0zph_6RBw fuuuuunky
return func(target string) macaron.Handler {
return func(c *models.ReqContext) {
limitReached, err := quotaService.QuotaReached(c, target)
if err != nil {
c.JsonApiErr(500, "failed to get quota", err)
c.JsonApiErr(500, "Failed to get quota", err)
return
}
if limitReached {
......
......@@ -10,37 +10,10 @@ import (
"github.com/grafana/grafana/pkg/services/quota"
"github.com/grafana/grafana/pkg/setting"
"github.com/stretchr/testify/assert"
macaron "gopkg.in/macaron.v1"
)
func TestMiddlewareQuota(t *testing.T) {
setting.AnonymousEnabled = false
setting.Quota = setting.QuotaSettings{
Enabled: true,
Org: &setting.OrgQuota{
User: 5,
Dashboard: 5,
DataSource: 5,
ApiKey: 5,
},
User: &setting.UserQuota{
Org: 5,
},
Global: &setting.GlobalQuota{
Org: 5,
User: 5,
Dashboard: 5,
DataSource: 5,
ApiKey: 5,
Session: 5,
},
}
fakeAuthTokenService := auth.NewFakeUserAuthTokenService()
qs := &quota.QuotaService{
AuthTokenService: fakeAuthTokenService,
}
quotaFn := Quota(qs)
t.Run("With user not logged in", func(t *testing.T) {
middlewareScenario(t, "and global quota not reached", func(t *testing.T, sc *scenarioContext) {
bus.AddHandler("globalQuota", func(query *models.GetGlobalQuotaByTargetQuery) error {
......@@ -52,10 +25,12 @@ func TestMiddlewareQuota(t *testing.T) {
return nil
})
sc.m.Get("/user", quotaFn("user"), sc.defaultHandler)
quotaHandler := getQuotaHandler(sc, "user")
sc.m.Get("/user", quotaHandler, sc.defaultHandler)
sc.fakeReq("GET", "/user").exec()
assert.Equal(t, 200, sc.resp.Code)
})
}, configure)
middlewareScenario(t, "and global quota reached", func(t *testing.T, sc *scenarioContext) {
bus.AddHandler("globalQuota", func(query *models.GetGlobalQuotaByTargetQuery) error {
......@@ -67,15 +42,14 @@ func TestMiddlewareQuota(t *testing.T) {
return nil
})
origUser := setting.Quota.Global.User
t.Cleanup(func() {
setting.Quota.Global.User = origUser
})
setting.Quota.Global.User = 4
sc.m.Get("/user", quotaFn("user"), sc.defaultHandler)
quotaHandler := getQuotaHandler(sc, "user")
sc.m.Get("/user", quotaHandler, sc.defaultHandler)
sc.fakeReq("GET", "/user").exec()
assert.Equal(t, 403, sc.resp.Code)
}, func(cfg *setting.Cfg) {
configure(cfg)
cfg.Quota.Global.User = 4
})
middlewareScenario(t, "and global session quota not reached", func(t *testing.T, sc *scenarioContext) {
......@@ -88,112 +62,185 @@ func TestMiddlewareQuota(t *testing.T) {
return nil
})
origSession := setting.Quota.Global.Session
t.Cleanup(func() {
setting.Quota.Global.Session = origSession
})
setting.Quota.Global.Session = 10
sc.m.Get("/user", quotaFn("session"), sc.defaultHandler)
quotaHandler := getQuotaHandler(sc, "session")
sc.m.Get("/user", quotaHandler, sc.defaultHandler)
sc.fakeReq("GET", "/user").exec()
assert.Equal(t, 200, sc.resp.Code)
}, func(cfg *setting.Cfg) {
configure(cfg)
cfg.Quota.Global.Session = 10
})
middlewareScenario(t, "and global session quota reached", func(t *testing.T, sc *scenarioContext) {
origSession := setting.Quota.Global.Session
t.Cleanup(func() {
setting.Quota.Global.Session = origSession
})
setting.Quota.Global.Session = 1
sc.m.Get("/user", quotaFn("session"), sc.defaultHandler)
quotaHandler := getQuotaHandler(sc, "session")
sc.m.Get("/user", quotaHandler, sc.defaultHandler)
sc.fakeReq("GET", "/user").exec()
assert.Equal(t, 403, sc.resp.Code)
}, func(cfg *setting.Cfg) {
configure(cfg)
cfg.Quota.Global.Session = 1
})
})
middlewareScenario(t, "with user logged in", func(t *testing.T, sc *scenarioContext) {
sc.withTokenSessionCookie("token")
bus.AddHandler("test", func(query *models.GetSignedInUserQuery) error {
query.Result = &models.SignedInUser{OrgId: 2, UserId: 12}
return nil
})
t.Run("with user logged in", func(t *testing.T) {
const quotaUsed = 4
sc.userAuthTokenService.LookupTokenProvider = func(ctx context.Context, unhashedToken string) (*models.UserToken, error) {
return &models.UserToken{
UserId: 12,
UnhashedToken: "",
}, nil
}
setUp := func(sc *scenarioContext) {
sc.withTokenSessionCookie("token")
bus.AddHandler("test", func(query *models.GetSignedInUserQuery) error {
query.Result = &models.SignedInUser{OrgId: 2, UserId: 12}
return nil
})
bus.AddHandler("globalQuota", func(query *models.GetGlobalQuotaByTargetQuery) error {
query.Result = &models.GlobalQuotaDTO{
Target: query.Target,
Limit: query.Default,
Used: 4,
sc.userAuthTokenService.LookupTokenProvider = func(ctx context.Context, unhashedToken string) (*models.UserToken, error) {
return &models.UserToken{
UserId: 12,
UnhashedToken: "",
}, nil
}
return nil
})
bus.AddHandler("userQuota", func(query *models.GetUserQuotaByTargetQuery) error {
query.Result = &models.UserQuotaDTO{
Target: query.Target,
Limit: query.Default,
Used: 4,
}
return nil
})
bus.AddHandler("globalQuota", func(query *models.GetGlobalQuotaByTargetQuery) error {
query.Result = &models.GlobalQuotaDTO{
Target: query.Target,
Limit: query.Default,
Used: quotaUsed,
}
return nil
})
bus.AddHandler("orgQuota", func(query *models.GetOrgQuotaByTargetQuery) error {
query.Result = &models.OrgQuotaDTO{
Target: query.Target,
Limit: query.Default,
Used: 4,
}
return nil
})
bus.AddHandler("userQuota", func(query *models.GetUserQuotaByTargetQuery) error {
query.Result = &models.UserQuotaDTO{
Target: query.Target,
Limit: query.Default,
Used: quotaUsed,
}
return nil
})
t.Run("global datasource quota reached", func(t *testing.T) {
setting.Quota.Global.DataSource = 4
sc.m.Get("/ds", quotaFn("data_source"), sc.defaultHandler)
bus.AddHandler("orgQuota", func(query *models.GetOrgQuotaByTargetQuery) error {
query.Result = &models.OrgQuotaDTO{
Target: query.Target,
Limit: query.Default,
Used: quotaUsed,
}
return nil
})
}
middlewareScenario(t, "global datasource quota reached", func(t *testing.T, sc *scenarioContext) {
setUp(sc)
quotaHandler := getQuotaHandler(sc, "data_source")
sc.m.Get("/ds", quotaHandler, sc.defaultHandler)
sc.fakeReq("GET", "/ds").exec()
assert.Equal(t, 403, sc.resp.Code)
}, func(cfg *setting.Cfg) {
configure(cfg)
cfg.Quota.Global.DataSource = quotaUsed
})
t.Run("user Org quota not reached", func(t *testing.T) {
setting.Quota.User.Org = 5
sc.m.Get("/org", quotaFn("org"), sc.defaultHandler)
middlewareScenario(t, "user Org quota not reached", func(t *testing.T, sc *scenarioContext) {
setUp(sc)
quotaHandler := getQuotaHandler(sc, "org")
sc.m.Get("/org", quotaHandler, sc.defaultHandler)
sc.fakeReq("GET", "/org").exec()
assert.Equal(t, 200, sc.resp.Code)
}, func(cfg *setting.Cfg) {
configure(cfg)
cfg.Quota.User.Org = quotaUsed + 1
})
t.Run("user Org quota reached", func(t *testing.T) {
setting.Quota.User.Org = 4
sc.m.Get("/org", quotaFn("org"), sc.defaultHandler)
middlewareScenario(t, "user Org quota reached", func(t *testing.T, sc *scenarioContext) {
setUp(sc)
quotaHandler := getQuotaHandler(sc, "org")
sc.m.Get("/org", quotaHandler, sc.defaultHandler)
sc.fakeReq("GET", "/org").exec()
assert.Equal(t, 403, sc.resp.Code)
}, func(cfg *setting.Cfg) {
configure(cfg)
cfg.Quota.User.Org = quotaUsed
})
t.Run("org dashboard quota not reached", func(t *testing.T) {
setting.Quota.Org.Dashboard = 10
sc.m.Get("/dashboard", quotaFn("dashboard"), sc.defaultHandler)
middlewareScenario(t, "org dashboard quota not reached", func(t *testing.T, sc *scenarioContext) {
setUp(sc)
quotaHandler := getQuotaHandler(sc, "dashboard")
sc.m.Get("/dashboard", quotaHandler, sc.defaultHandler)
sc.fakeReq("GET", "/dashboard").exec()
assert.Equal(t, 200, sc.resp.Code)
}, func(cfg *setting.Cfg) {
configure(cfg)
cfg.Quota.Org.Dashboard = quotaUsed + 1
})
t.Run("org dashboard quota reached", func(t *testing.T) {
setting.Quota.Org.Dashboard = 4
sc.m.Get("/dashboard", quotaFn("dashboard"), sc.defaultHandler)
middlewareScenario(t, "org dashboard quota reached", func(t *testing.T, sc *scenarioContext) {
setUp(sc)
quotaHandler := getQuotaHandler(sc, "dashboard")
sc.m.Get("/dashboard", quotaHandler, sc.defaultHandler)
sc.fakeReq("GET", "/dashboard").exec()
assert.Equal(t, 403, sc.resp.Code)
}, func(cfg *setting.Cfg) {
configure(cfg)
cfg.Quota.Org.Dashboard = quotaUsed
})
t.Run("org dashboard quota reached but quotas disabled", func(t *testing.T) {
setting.Quota.Org.Dashboard = 4
setting.Quota.Enabled = false
sc.m.Get("/dashboard", quotaFn("dashboard"), sc.defaultHandler)
middlewareScenario(t, "org dashboard quota reached, but quotas disabled", func(t *testing.T, sc *scenarioContext) {
setUp(sc)
quotaHandler := getQuotaHandler(sc, "dashboard")
sc.m.Get("/dashboard", quotaHandler, sc.defaultHandler)
sc.fakeReq("GET", "/dashboard").exec()
assert.Equal(t, 200, sc.resp.Code)
}, func(cfg *setting.Cfg) {
configure(cfg)
cfg.Quota.Org.Dashboard = quotaUsed
cfg.Quota.Enabled = false
})
})
}
func getQuotaHandler(sc *scenarioContext, target string) macaron.Handler {
fakeAuthTokenService := auth.NewFakeUserAuthTokenService()
qs := &quota.QuotaService{
AuthTokenService: fakeAuthTokenService,
Cfg: sc.cfg,
}
return Quota(qs)(target)
}
func configure(cfg *setting.Cfg) {
cfg.AnonymousEnabled = false
cfg.Quota = setting.QuotaSettings{
Enabled: true,
Org: &setting.OrgQuota{
User: 5,
Dashboard: 5,
DataSource: 5,
ApiKey: 5,
},
User: &setting.UserQuota{
Org: 5,
},
Global: &setting.GlobalQuota{
Org: 5,
User: 5,
Dashboard: 5,
DataSource: 5,
ApiKey: 5,
Session: 5,
},
}
}
......@@ -18,7 +18,7 @@ import (
func TestRecoveryMiddleware(t *testing.T) {
t.Run("Given an API route that panics", func(t *testing.T) {
apiURL := "/api/whatever"
recoveryScenario(t, "recovery middleware should return json", apiURL, func(t *testing.T, sc *scenarioContext) {
recoveryScenario(t, "recovery middleware should return JSON", apiURL, func(t *testing.T, sc *scenarioContext) {
sc.handlerFunc = panicHandler
sc.fakeReq("GET", apiURL).exec()
sc.req.Header.Set("content-type", "application/json")
......@@ -37,7 +37,7 @@ func TestRecoveryMiddleware(t *testing.T) {
assert.Equal(t, 500, sc.resp.Code)
assert.Equal(t, "text/html; charset=UTF-8", sc.resp.Header().Get("content-type"))
assert.True(t, strings.Contains(sc.resp.Body.String(), "<title>Grafana - Error</title>"))
assert.Contains(t, sc.resp.Body.String(), "<title>Grafana - Error</title>")
})
})
}
......@@ -76,7 +76,7 @@ func recoveryScenario(t *testing.T, desc string, url string, fn scenarioFunc) {
contextHandler := getContextHandler(t, nil)
sc.m.Use(contextHandler.Middleware)
// mock out gc goroutine
sc.m.Use(OrgRedirect())
sc.m.Use(OrgRedirect(cfg))
sc.defaultHandler = func(c *models.ReqContext) {
sc.context = c
......
......@@ -67,6 +67,8 @@ func (sc *scenarioContext) fakeReqWithParams(method, url string, queryParams map
sc.resp = httptest.NewRecorder()
req, err := http.NewRequest(method, url, nil)
require.NoError(sc.t, err)
q := req.URL.Query()
for k, v := range queryParams {
q.Add(k, v)
......@@ -78,11 +80,6 @@ func (sc *scenarioContext) fakeReqWithParams(method, url string, queryParams map
return sc
}
func (sc *scenarioContext) handler(fn handlerFunc) *scenarioContext {
sc.handlerFunc = fn
return sc
}
func (sc *scenarioContext) exec() {
sc.t.Helper()
......@@ -109,6 +106,9 @@ func (sc *scenarioContext) exec() {
if sc.resp.Header().Get("Content-Type") == "application/json; charset=UTF-8" {
err := json.NewDecoder(sc.resp.Body).Decode(&sc.respJson)
require.NoError(sc.t, err)
sc.t.Log("Decoded JSON", "json", sc.respJson)
} else {
sc.t.Log("Not decoding JSON")
}
}
......
......@@ -8,7 +8,7 @@ import (
"gopkg.in/macaron.v1"
)
func ValidateHostHeader(domain string) macaron.Handler {
func ValidateHostHeader(cfg *setting.Cfg) macaron.Handler {
return func(c *models.ReqContext) {
// ignore local render calls
if c.IsRenderCall {
......@@ -20,8 +20,8 @@ func ValidateHostHeader(domain string) macaron.Handler {
h = h[:i]
}
if !strings.EqualFold(h, domain) {
c.Redirect(strings.TrimSuffix(setting.AppUrl, "/")+c.Req.RequestURI, 301)
if !strings.EqualFold(h, cfg.Domain) {
c.Redirect(strings.TrimSuffix(cfg.AppURL, "/")+c.Req.RequestURI, 301)
return
}
}
......
......@@ -22,7 +22,7 @@ type ReqContext struct {
}
// Handle handles and logs error by given status.
func (ctx *ReqContext) Handle(status int, title string, err error) {
func (ctx *ReqContext) Handle(cfg *setting.Cfg, status int, title string, err error) {
if err != nil {
ctx.Logger.Error(title, "error", err)
if setting.Env != setting.Prod {
......@@ -31,10 +31,10 @@ func (ctx *ReqContext) Handle(status int, title string, err error) {
}
ctx.Data["Title"] = title
ctx.Data["AppSubUrl"] = setting.AppSubUrl
ctx.Data["AppSubUrl"] = cfg.AppSubURL
ctx.Data["Theme"] = "dark"
ctx.HTML(status, setting.ErrTemplateName)
ctx.HTML(status, cfg.ErrTemplateName)
}
func (ctx *ReqContext) IsApiRequest() bool {
......
......@@ -307,17 +307,17 @@ func GetDashboardFolderUrl(isFolder bool, uid string, slug string) string {
return GetDashboardUrl(uid, slug)
}
// GetDashboardUrl return the html url for a dashboard
// GetDashboardUrl returns the HTML url for a dashboard.
func GetDashboardUrl(uid string, slug string) string {
return fmt.Sprintf("%s/d/%s/%s", setting.AppSubUrl, uid, slug)
}
// GetFullDashboardUrl return the full url for a dashboard
// GetFullDashboardUrl returns the full URL for a dashboard.
func GetFullDashboardUrl(uid string, slug string) string {
return fmt.Sprintf("%sd/%s/%s", setting.AppUrl, uid, slug)
}
// GetFolderUrl return the html url for a folder
// GetFolderUrl returns the HTML url for a folder.
func GetFolderUrl(folderUid string, slug string) string {
return fmt.Sprintf("%s/dashboards/f/%s/%s", setting.AppSubUrl, folderUid, slug)
}
......
......@@ -3,8 +3,6 @@ package models
import (
"errors"
"time"
"github.com/grafana/grafana/pkg/setting"
)
var ErrInvalidQuotaTarget = errors.New("invalid quota target")
......@@ -86,46 +84,3 @@ type UpdateUserQuotaCmd struct {
Limit int64 `json:"limit"`
UserId int64 `json:"-"`
}
func GetQuotaScopes(target string) ([]QuotaScope, error) {
scopes := make([]QuotaScope, 0)
switch target {
case "user":
scopes = append(scopes,
QuotaScope{Name: "global", Target: target, DefaultLimit: setting.Quota.Global.User},
QuotaScope{Name: "org", Target: "org_user", DefaultLimit: setting.Quota.Org.User},
)
return scopes, nil
case "org":
scopes = append(scopes,
QuotaScope{Name: "global", Target: target, DefaultLimit: setting.Quota.Global.Org},
QuotaScope{Name: "user", Target: "org_user", DefaultLimit: setting.Quota.User.Org},
)
return scopes, nil
case "dashboard":
scopes = append(scopes,
QuotaScope{Name: "global", Target: target, DefaultLimit: setting.Quota.Global.Dashboard},
QuotaScope{Name: "org", Target: target, DefaultLimit: setting.Quota.Org.Dashboard},
)
return scopes, nil
case "data_source":
scopes = append(scopes,
QuotaScope{Name: "global", Target: target, DefaultLimit: setting.Quota.Global.DataSource},
QuotaScope{Name: "org", Target: target, DefaultLimit: setting.Quota.Org.DataSource},
)
return scopes, nil
case "api_key":
scopes = append(scopes,
QuotaScope{Name: "global", Target: target, DefaultLimit: setting.Quota.Global.ApiKey},
QuotaScope{Name: "org", Target: target, DefaultLimit: setting.Quota.Org.ApiKey},
)
return scopes, nil
case "session":
scopes = append(scopes,
QuotaScope{Name: "global", Target: target, DefaultLimit: setting.Quota.Global.Session},
)
return scopes, nil
default:
return scopes, ErrInvalidQuotaTarget
}
}
......@@ -81,7 +81,7 @@ type Options struct {
OrgID int64
}
// New instance of the AuthProxy
// New instance of the AuthProxy.
func New(cfg *setting.Cfg, options *Options) *AuthProxy {
header := options.Ctx.Req.Header.Get(cfg.AuthProxyHeaderName)
return &AuthProxy{
......@@ -93,7 +93,7 @@ func New(cfg *setting.Cfg, options *Options) *AuthProxy {
}
}
// IsEnabled checks if the proxy auth is enabled
// IsEnabled checks if the auth proxy is enabled.
func (auth *AuthProxy) IsEnabled() bool {
// Bail if the setting is not enabled
return auth.cfg.AuthProxyEnabled
......
......@@ -350,13 +350,13 @@ func logUserIn(auth *authproxy.AuthProxy, username string, logger log.Logger, ig
return id, nil
}
func handleError(ctx *models.ReqContext, err error, statusCode int, cb func(error)) {
func (h *ContextHandler) handleError(ctx *models.ReqContext, err error, statusCode int, cb func(error)) {
details := err
var e authproxy.Error
if errors.As(err, &e) {
details = e.DetailsError
}
ctx.Handle(statusCode, err.Error(), details)
ctx.Handle(h.Cfg, statusCode, err.Error(), details)
if cb != nil {
cb(details)
......@@ -385,7 +385,7 @@ func (h *ContextHandler) initContextWithAuthProxy(ctx *models.ReqContext, orgID
// Check if allowed to continue with this IP
if err := auth.IsAllowedIP(); err != nil {
handleError(ctx, err, 407, func(details error) {
h.handleError(ctx, err, 407, func(details error) {
logger.Error("Failed to check whitelisted IP addresses", "message", err.Error(), "error", details)
})
return true
......@@ -393,7 +393,7 @@ func (h *ContextHandler) initContextWithAuthProxy(ctx *models.ReqContext, orgID
id, err := logUserIn(auth, username, logger, false)
if err != nil {
handleError(ctx, err, 407, nil)
h.handleError(ctx, err, 407, nil)
return true
}
......@@ -414,13 +414,13 @@ func (h *ContextHandler) initContextWithAuthProxy(ctx *models.ReqContext, orgID
}
id, err = logUserIn(auth, username, logger, true)
if err != nil {
handleError(ctx, err, 407, nil)
h.handleError(ctx, err, 407, nil)
return true
}
user, err = auth.GetSignedInUser(id)
if err != nil {
handleError(ctx, err, 407, nil)
h.handleError(ctx, err, 407, nil)
return true
}
}
......@@ -433,7 +433,7 @@ func (h *ContextHandler) initContextWithAuthProxy(ctx *models.ReqContext, orgID
// Remember user data in cache
if err := auth.Remember(id); err != nil {
handleError(ctx, err, 500, func(details error) {
h.handleError(ctx, err, 500, func(details error) {
logger.Error(
"Failed to store user in cache",
"username", username,
......
......@@ -11,7 +11,6 @@ import (
"github.com/grafana/grafana/pkg/infra/log"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/services/auth"
"github.com/grafana/grafana/pkg/setting"
"github.com/grafana/grafana/pkg/util"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
......@@ -22,7 +21,7 @@ func TestDontRotateTokensOnCancelledRequests(t *testing.T) {
ctxHdlr := getContextHandler(t)
ctx, cancel := context.WithCancel(context.Background())
reqContext, _, err := initTokenRotationScenario(ctx, t)
reqContext, _, err := initTokenRotationScenario(ctx, t, ctxHdlr)
require.NoError(t, err)
tryRotateCallCount := 0
......@@ -46,7 +45,7 @@ func TestDontRotateTokensOnCancelledRequests(t *testing.T) {
func TestTokenRotationAtEndOfRequest(t *testing.T) {
ctxHdlr := getContextHandler(t)
reqContext, rr, err := initTokenRotationScenario(context.Background(), t)
reqContext, rr, err := initTokenRotationScenario(context.Background(), t, ctxHdlr)
require.NoError(t, err)
uts := &auth.FakeUserAuthTokenService{
......@@ -80,18 +79,13 @@ func TestTokenRotationAtEndOfRequest(t *testing.T) {
assert.True(t, foundLoginCookie, "Could not find cookie")
}
func initTokenRotationScenario(ctx context.Context, t *testing.T) (*models.ReqContext, *httptest.ResponseRecorder, error) {
func initTokenRotationScenario(ctx context.Context, t *testing.T, ctxHdlr *ContextHandler) (
*models.ReqContext, *httptest.ResponseRecorder, error) {
t.Helper()
origLoginCookieName := setting.LoginCookieName
origLoginMaxLifetime := setting.LoginMaxLifetime
t.Cleanup(func() {
setting.LoginCookieName = origLoginCookieName
setting.LoginMaxLifetime = origLoginMaxLifetime
})
setting.LoginCookieName = "login_token"
ctxHdlr.Cfg.LoginCookieName = "login_token"
var err error
setting.LoginMaxLifetime, err = gtime.ParseDuration("7d")
ctxHdlr.Cfg.LoginMaxLifetime, err = gtime.ParseDuration("7d")
if err != nil {
return nil, nil, err
}
......
package quota
import (
"errors"
"github.com/grafana/grafana/pkg/bus"
"github.com/grafana/grafana/pkg/models"
"github.com/grafana/grafana/pkg/registry"
"github.com/grafana/grafana/pkg/setting"
)
var ErrInvalidQuotaTarget = errors.New("invalid quota target")
func init() {
registry.RegisterService(&QuotaService{})
}
type QuotaService struct {
AuthTokenService models.UserTokenService `inject:""`
Cfg *setting.Cfg `inject:""`
}
func (qs *QuotaService) Init() error {
......@@ -20,7 +25,7 @@ func (qs *QuotaService) Init() error {
}
func (qs *QuotaService) QuotaReached(c *models.ReqContext, target string) (bool, error) {
if !setting.Quota.Enabled {
if !qs.Cfg.Quota.Enabled {
return false, nil
}
// No request context means this is a background service, like LDAP Background Sync.
......@@ -29,8 +34,9 @@ func (qs *QuotaService) QuotaReached(c *models.ReqContext, target string) (bool,
if c == nil {
return false, nil
}
// get the list of scopes that this target is valid for. Org, User, Global
scopes, err := models.GetQuotaScopes(target)
scopes, err := qs.getQuotaScopes(target)
if err != nil {
return false, err
}
......@@ -106,3 +112,46 @@ func (qs *QuotaService) QuotaReached(c *models.ReqContext, target string) (bool,
return false, nil
}
func (qs *QuotaService) getQuotaScopes(target string) ([]models.QuotaScope, error) {
scopes := make([]models.QuotaScope, 0)
switch target {
case "user":
scopes = append(scopes,
models.QuotaScope{Name: "global", Target: target, DefaultLimit: qs.Cfg.Quota.Global.User},
models.QuotaScope{Name: "org", Target: "org_user", DefaultLimit: qs.Cfg.Quota.Org.User},
)
return scopes, nil
case "org":
scopes = append(scopes,
models.QuotaScope{Name: "global", Target: target, DefaultLimit: qs.Cfg.Quota.Global.Org},
models.QuotaScope{Name: "user", Target: "org_user", DefaultLimit: qs.Cfg.Quota.User.Org},
)
return scopes, nil
case "dashboard":
scopes = append(scopes,
models.QuotaScope{Name: "global", Target: target, DefaultLimit: qs.Cfg.Quota.Global.Dashboard},
models.QuotaScope{Name: "org", Target: target, DefaultLimit: qs.Cfg.Quota.Org.Dashboard},
)
return scopes, nil
case "data_source":
scopes = append(scopes,
models.QuotaScope{Name: "global", Target: target, DefaultLimit: qs.Cfg.Quota.Global.DataSource},
models.QuotaScope{Name: "org", Target: target, DefaultLimit: qs.Cfg.Quota.Org.DataSource},
)
return scopes, nil
case "api_key":
scopes = append(scopes,
models.QuotaScope{Name: "global", Target: target, DefaultLimit: qs.Cfg.Quota.Global.ApiKey},
models.QuotaScope{Name: "org", Target: target, DefaultLimit: qs.Cfg.Quota.Org.ApiKey},
)
return scopes, nil
case "session":
scopes = append(scopes,
models.QuotaScope{Name: "global", Target: target, DefaultLimit: qs.Cfg.Quota.Global.Session},
)
return scopes, nil
default:
return scopes, ErrInvalidQuotaTarget
}
}
package sqlstore
import (
"context"
"fmt"
"time"
......@@ -275,14 +274,3 @@ func getOrCreateOrg(sess *DBSession, orgName string) (int64, error) {
return org.Id, nil
}
func createDefaultOrg(ctx context.Context) error {
return inTransactionCtx(ctx, func(sess *DBSession) error {
_, err := getOrCreateOrg(sess, mainOrgName)
if err != nil {
return err
}
return nil
})
}
......@@ -118,10 +118,13 @@ func (ss *SQLStore) Init() error {
func (ss *SQLStore) ensureMainOrgAndAdminUser() error {
err := ss.InTransaction(context.Background(), func(ctx context.Context) error {
ss.log.Debug("Ensuring main org and admin user exist")
var stats models.SystemUserCountStats
err := ss.WithDbSession(ctx, func(sess *DBSession) error {
var rawSql = `SELECT COUNT(id) AS Count FROM ` + dialect.Quote("user")
if _, err := sess.SQL(rawSql).Get(&stats); err != nil {
// TODO: Should be able to rename "Count" to "count", for more standard SQL style
// Just have to make sure it gets deserialized properly into models.SystemUserCountStats
rawSQL := `SELECT COUNT(id) AS Count FROM ` + dialect.Quote("user")
if _, err := sess.SQL(rawSQL).Get(&stats); err != nil {
return fmt.Errorf("could not determine if admin user exists: %w", err)
}
......@@ -137,23 +140,27 @@ func (ss *SQLStore) ensureMainOrgAndAdminUser() error {
// ensure admin user
if !ss.Cfg.DisableInitAdminCreation {
cmd := models.CreateUserCommand{}
cmd.Login = setting.AdminUser
cmd.Email = setting.AdminUser + "@localhost"
cmd.Password = setting.AdminPassword
cmd.IsAdmin = true
ss.log.Debug("Creating default admin user")
cmd := models.CreateUserCommand{
Login: ss.Cfg.AdminUser,
Email: ss.Cfg.AdminUser + "@localhost",
Password: ss.Cfg.AdminPassword,
IsAdmin: true,
}
if err := bus.DispatchCtx(ctx, &cmd); err != nil {
return fmt.Errorf("failed to create admin user: %s", err)
}
ss.log.Info("Created default admin", "user", setting.AdminUser)
ss.log.Info("Created default admin", "user", ss.Cfg.AdminUser)
return nil
}
// ensure default org if default admin user is disabled
if err := createDefaultOrg(ctx); err != nil {
return errutil.Wrap("Failed to create default organization", err)
// ensure default org even if default admin user is disabled
if err := inTransactionCtx(ctx, func(sess *DBSession) error {
_, err := getOrCreateOrg(sess, mainOrgName)
return err
}); err != nil {
return fmt.Errorf("failed to create default organization: %w", err)
}
ss.log.Info("Created default organization")
......
......@@ -40,10 +40,6 @@ const (
Test = "test"
)
var (
ErrTemplateName = "error"
)
// This constant corresponds to the default value for ldap_sync_ttl in .ini files
// it is used for comparison and has to be kept in sync
const (
......@@ -126,10 +122,6 @@ var (
ViewersCanEdit bool
// HTTP auth
AdminUser string
AdminPassword string
LoginCookieName string
LoginMaxLifetime time.Duration
SigV4AuthEnabled bool
AnonymousEnabled bool
......@@ -271,6 +263,8 @@ type Cfg struct {
TokenRotationIntervalMinutes int
SigV4AuthEnabled bool
BasicAuthEnabled bool
AdminUser string
AdminPassword string
// Auth proxy settings
AuthProxyEnabled bool
......@@ -724,7 +718,7 @@ func (cfg *Cfg) Load(args *CommandLineArgs) error {
cfg.IsEnterprise = IsEnterprise
cfg.Packaging = Packaging
cfg.ErrTemplateName = ErrTemplateName
cfg.ErrTemplateName = "error"
ApplicationName = "Grafana"
......@@ -1013,8 +1007,8 @@ func readSecuritySettings(iniFile *ini.File, cfg *Cfg) error {
// admin
cfg.DisableInitAdminCreation = security.Key("disable_initial_admin_creation").MustBool(false)
AdminUser = valueAsString(security, "admin_user", "")
AdminPassword = valueAsString(security, "admin_password", "")
cfg.AdminUser = valueAsString(security, "admin_user", "")
cfg.AdminPassword = valueAsString(security, "admin_password", "")
return nil
}
......@@ -1022,8 +1016,7 @@ func readSecuritySettings(iniFile *ini.File, cfg *Cfg) error {
func readAuthSettings(iniFile *ini.File, cfg *Cfg) (err error) {
auth := iniFile.Section("auth")
LoginCookieName = valueAsString(auth, "login_cookie_name", "grafana_session")
cfg.LoginCookieName = LoginCookieName
cfg.LoginCookieName = valueAsString(auth, "login_cookie_name", "grafana_session")
maxInactiveDaysVal := auth.Key("login_maximum_inactive_lifetime_days").MustString("")
if maxInactiveDaysVal != "" {
maxInactiveDaysVal = fmt.Sprintf("%sd", maxInactiveDaysVal)
......@@ -1049,7 +1042,6 @@ func readAuthSettings(iniFile *ini.File, cfg *Cfg) (err error) {
if err != nil {
return err
}
LoginMaxLifetime = cfg.LoginMaxLifetime
cfg.ApiKeyMaxSecondsToLive = auth.Key("api_key_max_seconds_to_live").MustInt64(-1)
......
......@@ -30,7 +30,7 @@ func TestLoadingSettings(t *testing.T) {
err := cfg.Load(&CommandLineArgs{HomePath: "../../"})
So(err, ShouldBeNil)
So(AdminUser, ShouldEqual, "admin")
So(cfg.AdminUser, ShouldEqual, "admin")
So(cfg.RendererCallbackUrl, ShouldEqual, "http://localhost:3000/")
})
......@@ -61,7 +61,7 @@ func TestLoadingSettings(t *testing.T) {
err = cfg.Load(&CommandLineArgs{HomePath: "../../"})
So(err, ShouldBeNil)
So(AdminUser, ShouldEqual, "superduper")
So(cfg.AdminUser, ShouldEqual, "superduper")
So(cfg.DataPath, ShouldEqual, filepath.Join(HomePath, "data"))
So(cfg.LogsPath, ShouldEqual, filepath.Join(cfg.DataPath, "log"))
})
......
......@@ -452,7 +452,7 @@ func isTerminated(queryStatus string) bool {
return queryStatus == "Complete" || queryStatus == "Cancelled" || queryStatus == "Failed" || queryStatus == "Timeout"
}
// CloudWatch client factory.
// newCWClient is a CloudWatch client factory.
//
// Stubbable by tests.
var newCWClient = func(sess *session.Session) cloudwatchiface.CloudWatchAPI {
......@@ -464,7 +464,7 @@ var newCWClient = func(sess *session.Session) cloudwatchiface.CloudWatchAPI {
return client
}
// CloudWatch logs client factory.
// newCWLogsClient is a CloudWatch logs client factory.
//
// Stubbable by tests.
var newCWLogsClient = func(sess *session.Session) cloudwatchlogsiface.CloudWatchLogsAPI {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment