From 30f5445bcdb33a2f0ea39beb3b78cc73443e666a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?G=C3=A1bor=20Farkas?= Date: Thu, 20 Mar 2025 11:14:53 +0100 Subject: [PATCH] datasources: querier: single-tenant: explicit permission-check (#102189) --- pkg/registry/apis/query/client/plugin.go | 40 ++++++++++++++++++------ pkg/registry/apis/query/register.go | 2 +- 2 files changed, 31 insertions(+), 11 deletions(-) diff --git a/pkg/registry/apis/query/client/plugin.go b/pkg/registry/apis/query/client/plugin.go index 434f3b049f9..7301f819be7 100644 --- a/pkg/registry/apis/query/client/plugin.go +++ b/pkg/registry/apis/query/client/plugin.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 } diff --git a/pkg/registry/apis/query/register.go b/pkg/registry/apis/query/register.go index 44f15087832..83ca935a662 100644 --- a/pkg/registry/apis/query/register.go +++ b/pkg/registry/apis/query/register.go @@ -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),