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

This commit is contained in:
Gábor Farkas
2025-03-20 11:14:53 +01:00
committed by GitHub
parent 5b67ae1876
commit 30f5445bcd
2 changed files with 31 additions and 11 deletions
+30 -10
View File
@@ -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
}