datasources: querier: single-tenant: explicit permission-check (#102189)

pull/102525/head
Gábor Farkas 4 months ago committed by GitHub
parent 5b67ae1876
commit 30f5445bcd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 40
      pkg/registry/apis/query/client/plugin.go
  2. 2
      pkg/registry/apis/query/register.go

@ -2,7 +2,6 @@ package client
import (
"context"
"errors"
"fmt"
"net/http"
"sync"
@ -13,9 +12,11 @@ import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"github.com/grafana/grafana/pkg/apimachinery/identity"
query "github.com/grafana/grafana/pkg/apis/query/v0alpha1"
"github.com/grafana/grafana/pkg/plugins"
"github.com/grafana/grafana/pkg/registry/apis/query/clientapi"
"github.com/grafana/grafana/pkg/services/accesscontrol"
"github.com/grafana/grafana/pkg/services/datasources"
"github.com/grafana/grafana/pkg/services/pluginsintegration/plugincontext"
"github.com/grafana/grafana/pkg/services/pluginsintegration/pluginstore"
@ -26,6 +27,7 @@ import (
type pluginClient struct {
pluginClient plugins.Client
pCtxProvider *plugincontext.Provider
ac accesscontrol.AccessControl
}
type pluginRegistry struct {
@ -43,10 +45,11 @@ var _ clientapi.QueryDataClient = (*pluginClient)(nil)
var _ query.DataSourceApiServerRegistry = (*pluginRegistry)(nil)
// NewQueryClientForPluginClient creates a client that delegates to the internal plugins.Client stack
func NewQueryClientForPluginClient(p plugins.Client, ctx *plugincontext.Provider) clientapi.QueryDataClient {
func NewQueryClientForPluginClient(p plugins.Client, ctx *plugincontext.Provider, accessControl accesscontrol.AccessControl) clientapi.QueryDataClient {
return &pluginClient{
pluginClient: p,
pCtxProvider: ctx,
ac: accessControl,
}
}
@ -59,6 +62,17 @@ func NewDataSourceRegistryFromStore(pluginStore pluginstore.Store,
}
}
func (d *pluginClient) CanQueryDataSource(ctx context.Context, uid string) (bool, error) {
user, err := identity.GetRequester(ctx)
if err != nil {
return false, err
}
requiredScope := "datasources:uid:" + uid
evaluate := accesscontrol.EvalPermission(datasources.ActionQuery, requiredScope)
return d.ac.Evaluate(ctx, user, evaluate)
}
// ExecuteQueryData implements QueryHelper.
func (d *pluginClient) QueryData(ctx context.Context, req data.QueryDataRequest) (*backend.QueryDataResponse, error) {
queries, dsRef, err := data.ToDataSourceQueries(req)
@ -69,17 +83,23 @@ func (d *pluginClient) QueryData(ctx context.Context, req data.QueryDataRequest)
return nil, fmt.Errorf("expected single datasource request")
}
canQuery, err := d.CanQueryDataSource(ctx, dsRef.UID)
if err != nil {
return nil, err
}
if !canQuery {
status := metav1.Status{
Status: metav1.StatusFailure,
Code: http.StatusForbidden,
Message: "Access denied to the data source",
}
return nil, &apierrors.StatusError{ErrStatus: status}
}
// NOTE: this depends on uid unique across datasources
settings, err := d.pCtxProvider.GetDataSourceInstanceSettings(ctx, dsRef.UID)
if err != nil {
if errors.Is(err, datasources.ErrDataSourceNotFound) {
status := metav1.Status{
Status: metav1.StatusFailure,
Code: http.StatusNotFound,
Message: "datasource not found",
}
return nil, &apierrors.StatusError{ErrStatus: status}
}
return nil, err
}

@ -127,7 +127,7 @@ func RegisterAPIService(features featuremgmt.FeatureToggles,
builder, err := NewQueryAPIBuilder(
features,
&CommonDataSourceClientSupplier{
Client: client.NewQueryClientForPluginClient(pluginClient, pCtxProvider),
Client: client.NewQueryClientForPluginClient(pluginClient, pCtxProvider, accessControl),
},
ar,
client.NewDataSourceRegistryFromStore(pluginStore, dataSourcesService),

Loading…
Cancel
Save