mirror of
https://github.com/grafana/grafana.git
synced 2024-11-30 20:54:22 -06:00
6dbe3b555f
Adding support for backend plugin client middlewares. This allows headers in outgoing backend plugin and HTTP requests to be modified using client middlewares. The following client middlewares added: Forward cookies: Will forward incoming HTTP request Cookies to outgoing plugins.Client and HTTP requests if the datasource has enabled forwarding of cookies (keepCookies). Forward OAuth token: Will set OAuth token headers on outgoing plugins.Client and HTTP requests if the datasource has enabled Forward OAuth Identity (oauthPassThru). Clear auth headers: Will clear any outgoing HTTP headers that was part of the incoming HTTP request and used when authenticating to Grafana. The current suggested way to register client middlewares is to have a separate package, pluginsintegration, responsible for bootstrap/instantiate the backend plugin client with middlewares and/or longer term bootstrap/instantiate plugin management. Fixes #54135 Related to #47734 Related to #57870 Related to #41623 Related to #57065
164 lines
4.5 KiB
Go
164 lines
4.5 KiB
Go
package api
|
|
|
|
import (
|
|
"context"
|
|
"io"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
|
|
"github.com/grafana/grafana-plugin-sdk-go/backend"
|
|
"github.com/grafana/grafana-plugin-sdk-go/data"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/grafana/grafana/pkg/api/routing"
|
|
"github.com/grafana/grafana/pkg/infra/db"
|
|
"github.com/grafana/grafana/pkg/infra/localcache"
|
|
"github.com/grafana/grafana/pkg/infra/log"
|
|
"github.com/grafana/grafana/pkg/models"
|
|
"github.com/grafana/grafana/pkg/plugins"
|
|
"github.com/grafana/grafana/pkg/services/accesscontrol"
|
|
"github.com/grafana/grafana/pkg/services/accesscontrol/acimpl"
|
|
"github.com/grafana/grafana/pkg/services/accesscontrol/actest"
|
|
"github.com/grafana/grafana/pkg/services/contexthandler/ctxkey"
|
|
"github.com/grafana/grafana/pkg/services/datasources"
|
|
fakeDatasources "github.com/grafana/grafana/pkg/services/datasources/fakes"
|
|
datasourceService "github.com/grafana/grafana/pkg/services/datasources/service"
|
|
"github.com/grafana/grafana/pkg/services/featuremgmt"
|
|
"github.com/grafana/grafana/pkg/services/publicdashboards"
|
|
"github.com/grafana/grafana/pkg/services/query"
|
|
"github.com/grafana/grafana/pkg/services/user"
|
|
"github.com/grafana/grafana/pkg/setting"
|
|
"github.com/grafana/grafana/pkg/web"
|
|
)
|
|
|
|
func setupTestServer(
|
|
t *testing.T,
|
|
cfg *setting.Cfg,
|
|
features *featuremgmt.FeatureManager,
|
|
service publicdashboards.Service,
|
|
db db.DB,
|
|
user *user.SignedInUser,
|
|
) *web.Mux {
|
|
// build router to register routes
|
|
rr := routing.NewRouteRegister()
|
|
|
|
var permissions []accesscontrol.Permission
|
|
if user != nil && user.Permissions != nil {
|
|
for action, scopes := range user.Permissions[user.OrgID] {
|
|
for _, scope := range scopes {
|
|
permissions = append(permissions, accesscontrol.Permission{
|
|
Action: action,
|
|
Scope: scope,
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
acService := actest.FakeService{ExpectedPermissions: permissions, ExpectedDisabled: !cfg.RBACEnabled}
|
|
ac := acimpl.ProvideAccessControl(cfg)
|
|
|
|
// build mux
|
|
m := web.New()
|
|
|
|
// set initial context
|
|
m.Use(contextProvider(&testContext{user}))
|
|
m.Use(accesscontrol.LoadPermissionsMiddleware(acService))
|
|
|
|
// build api, this will mount the routes at the same time if
|
|
// featuremgmt.FlagPublicDashboard is enabled
|
|
ProvideApi(service, rr, ac, features)
|
|
|
|
// connect routes to mux
|
|
rr.Register(m.Router)
|
|
|
|
return m
|
|
}
|
|
|
|
type testContext struct {
|
|
user *user.SignedInUser
|
|
}
|
|
|
|
func contextProvider(tc *testContext) web.Handler {
|
|
return func(c *web.Context) {
|
|
signedIn := tc.user != nil
|
|
reqCtx := &models.ReqContext{
|
|
Context: c,
|
|
SignedInUser: tc.user,
|
|
IsSignedIn: signedIn,
|
|
SkipCache: true,
|
|
Logger: log.New("publicdashboards-test"),
|
|
}
|
|
c.Req = c.Req.WithContext(ctxkey.Set(c.Req.Context(), reqCtx))
|
|
}
|
|
}
|
|
|
|
func callAPI(server *web.Mux, method, path string, body io.Reader, t *testing.T) *httptest.ResponseRecorder {
|
|
req, err := http.NewRequest(method, path, body)
|
|
require.NoError(t, err)
|
|
req.Header.Set("Content-Type", "application/json")
|
|
recorder := httptest.NewRecorder()
|
|
server.ServeHTTP(recorder, req)
|
|
return recorder
|
|
}
|
|
|
|
// helper to query.Service
|
|
// allows us to stub the cache and plugin clients
|
|
func buildQueryDataService(t *testing.T, cs datasources.CacheService, fpc *fakePluginClient, store db.DB) *query.Service {
|
|
// build database if we need one
|
|
if store == nil {
|
|
store = db.InitTestDB(t)
|
|
}
|
|
|
|
// default cache service
|
|
if cs == nil {
|
|
cs = datasourceService.ProvideCacheService(localcache.ProvideService(), store)
|
|
}
|
|
|
|
// default fakePluginClient
|
|
if fpc == nil {
|
|
fpc = &fakePluginClient{
|
|
QueryDataHandlerFunc: func(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
|
|
resp := backend.Responses{
|
|
"A": backend.DataResponse{
|
|
Frames: []*data.Frame{{}},
|
|
},
|
|
}
|
|
return &backend.QueryDataResponse{Responses: resp}, nil
|
|
},
|
|
}
|
|
}
|
|
|
|
return query.ProvideService(
|
|
setting.NewCfg(),
|
|
cs,
|
|
nil,
|
|
&fakePluginRequestValidator{},
|
|
&fakeDatasources.FakeDataSourceService{},
|
|
fpc,
|
|
)
|
|
}
|
|
|
|
// copied from pkg/api/metrics_test.go
|
|
type fakePluginRequestValidator struct {
|
|
err error
|
|
}
|
|
|
|
func (rv *fakePluginRequestValidator) Validate(dsURL string, req *http.Request) error {
|
|
return rv.err
|
|
}
|
|
|
|
// copied from pkg/api/plugins_test.go
|
|
type fakePluginClient struct {
|
|
plugins.Client
|
|
backend.QueryDataHandlerFunc
|
|
}
|
|
|
|
func (c *fakePluginClient) QueryData(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) {
|
|
if c.QueryDataHandlerFunc != nil {
|
|
return c.QueryDataHandlerFunc.QueryData(ctx, req)
|
|
}
|
|
|
|
return backend.NewQueryDataResponse(), nil
|
|
}
|