From e270412dbf43bef815eebb43fe1948e240ea79a5 Mon Sep 17 00:00:00 2001 From: Georges Chaudy Date: Tue, 19 Nov 2024 14:13:30 +0100 Subject: [PATCH] unistore: wire the authz client (#96632) * unistore: wire the authz client * rename dashboards.grafana.app into dashboard.grafana.app * wire the authz client * wire the authz client * resuse the Standalone constructor * configure default migration for resource folder * add tests * cleanup * add logging --- pkg/services/apiserver/standalone/runtime.go | 2 +- .../apiserver/standalone/runtime_test.go | 4 +- pkg/services/authz/client.go | 5 +- pkg/services/authz/mappers/rbac_mapper.go | 8 +- pkg/services/authz/server_test.go | 16 ++-- .../zanzana/server/server_batch_check_test.go | 10 +-- .../authz/zanzana/server/server_check_test.go | 10 +-- .../authz/zanzana/server/server_list_test.go | 8 +- pkg/storage/unified/client.go | 4 +- pkg/storage/unified/resource/access.go | 81 +++++++++++++++++++ pkg/storage/unified/resource/access_test.go | 62 ++++++++++++++ pkg/storage/unified/sql/server.go | 7 +- pkg/storage/unified/sql/service.go | 8 +- .../features/dashboard/api/dashboard_api.ts | 2 +- 14 files changed, 192 insertions(+), 35 deletions(-) create mode 100644 pkg/storage/unified/resource/access_test.go diff --git a/pkg/services/apiserver/standalone/runtime.go b/pkg/services/apiserver/standalone/runtime.go index 7b8abcb6d93..f2284356d70 100644 --- a/pkg/services/apiserver/standalone/runtime.go +++ b/pkg/services/apiserver/standalone/runtime.go @@ -17,7 +17,7 @@ func (a RuntimeConfig) String() string { // Supported options are: // -// /=true|false for a specific API group and version (e.g. dashboards.grafana.app/v0alpha1=true) +// /=true|false for a specific API group and version (e.g. dashboard.grafana.app/v0alpha1=true) // api/all=true|false controls all API versions // api/ga=true|false controls all API versions of the form v[0-9]+ // api/beta=true|false controls all API versions of the form v[0-9]+beta[0-9]+ diff --git a/pkg/services/apiserver/standalone/runtime_test.go b/pkg/services/apiserver/standalone/runtime_test.go index 374920cfd0e..e07db7b62a6 100644 --- a/pkg/services/apiserver/standalone/runtime_test.go +++ b/pkg/services/apiserver/standalone/runtime_test.go @@ -8,11 +8,11 @@ import ( ) func TestReadRuntimeCOnfig(t *testing.T) { - out, err := ReadRuntimeConfig("all/all=true,dashboards.grafana.app/v0alpha1=false") + out, err := ReadRuntimeConfig("all/all=true,dashboard.grafana.app/v0alpha1=false") require.NoError(t, err) require.Equal(t, []RuntimeConfig{ {Group: "all", Version: "all", Enabled: true}, - {Group: "dashboards.grafana.app", Version: "v0alpha1", Enabled: false}, + {Group: "dashboard.grafana.app", Version: "v0alpha1", Enabled: false}, }, out) require.Equal(t, "all/all=true", fmt.Sprintf("%v", out[0])) diff --git a/pkg/services/authz/client.go b/pkg/services/authz/client.go index d0074e9e7d2..a692abdcc9c 100644 --- a/pkg/services/authz/client.go +++ b/pkg/services/authz/client.go @@ -88,7 +88,10 @@ func ProvideStandaloneAuthZClient( return nil, err } - return newGrpcLegacyClient(authCfg) + if cfg.StackID == "" { + return newGrpcLegacyClient(authCfg) + } + return newCloudLegacyClient(authCfg) } func newInProcLegacyClient(server *legacyServer) (authzlib.AccessChecker, error) { diff --git a/pkg/services/authz/mappers/rbac_mapper.go b/pkg/services/authz/mappers/rbac_mapper.go index e12e5d0ebcc..fa9a1a1a4eb 100644 --- a/pkg/services/authz/mappers/rbac_mapper.go +++ b/pkg/services/authz/mappers/rbac_mapper.go @@ -2,10 +2,10 @@ package mappers type VerbToAction map[string]string // e.g. "get" -> "read" type ResourceVerbToAction map[string]VerbToAction // e.g. "dashboards" -> VerbToAction -type GroupResourceVerbToAction map[string]ResourceVerbToAction // e.g. "dashboards.grafana.app" -> ResourceVerbToAction +type GroupResourceVerbToAction map[string]ResourceVerbToAction // e.g. "dashboard.grafana.app" -> ResourceVerbToAction type ResourceToAttribute map[string]string // e.g. "dashboards" -> "uid" -type GroupResourceToAttribute map[string]ResourceToAttribute // e.g. "dashboards.grafana.app" -> ResourceToAttribute +type GroupResourceToAttribute map[string]ResourceToAttribute // e.g. "dashboard.grafana.app" -> ResourceToAttribute type K8sRbacMapper struct { DefaultActions VerbToAction @@ -28,8 +28,8 @@ func NewK8sRbacMapper() *K8sRbacMapper { }, DefaultAttribute: "uid", Actions: GroupResourceVerbToAction{ - "dashboards.grafana.app": ResourceVerbToAction{"dashboards": VerbToAction{}}, - "folders.grafana.app": ResourceVerbToAction{"folders": VerbToAction{}}, + "dashboard.grafana.app": ResourceVerbToAction{"dashboards": VerbToAction{}}, + "folder.grafana.app": ResourceVerbToAction{"folders": VerbToAction{}}, }, } } diff --git a/pkg/services/authz/server_test.go b/pkg/services/authz/server_test.go index 4f5514b1aa2..1bd617ce6ed 100644 --- a/pkg/services/authz/server_test.go +++ b/pkg/services/authz/server_test.go @@ -60,7 +60,7 @@ func Test_legacyServer_Check(t *testing.T) { req: &authzv1.CheckRequest{ Subject: "user:1", Verb: "get", - Group: "dashboards.grafana.app", + Group: "dashboard.grafana.app", Resource: "dashboards", Name: "dash1", Namespace: "org-2", @@ -74,7 +74,7 @@ func Test_legacyServer_Check(t *testing.T) { req: &authzv1.CheckRequest{ Subject: "user:1", Verb: "get", - Group: "dashboards.grafana.app", + Group: "dashboard.grafana.app", Resource: "dashboards", Name: "dash1", Namespace: "org-2", @@ -88,7 +88,7 @@ func Test_legacyServer_Check(t *testing.T) { req: &authzv1.CheckRequest{ Subject: "user:1", Verb: "get", - Group: "dashboards.grafana.app", + Group: "dashboard.grafana.app", Resource: "dashboards", Name: "dash1", Namespace: "org-2", @@ -106,7 +106,7 @@ func Test_legacyServer_Check(t *testing.T) { req: &authzv1.CheckRequest{ Subject: "user:1", Verb: "get", - Group: "dashboards.grafana.app", + Group: "dashboard.grafana.app", Resource: "dashboards", Namespace: "org-2", }, @@ -131,7 +131,7 @@ func Test_legacyServer_Check(t *testing.T) { req: &authzv1.CheckRequest{ Subject: "user:1", Verb: "get", - Group: "dashboards.grafana.app", + Group: "dashboard.grafana.app", Name: "dash1", Namespace: "org-2", }, @@ -141,7 +141,7 @@ func Test_legacyServer_Check(t *testing.T) { name: "should return error when verb is not set", req: &authzv1.CheckRequest{ Subject: "user:1", - Group: "dashboards.grafana.app", + Group: "dashboard.grafana.app", Resource: "dashboards", Name: "dash1", Namespace: "org-2", @@ -152,7 +152,7 @@ func Test_legacyServer_Check(t *testing.T) { name: "should return error when subject is not set", req: &authzv1.CheckRequest{ Verb: "get", - Group: "dashboards.grafana.app", + Group: "dashboard.grafana.app", Resource: "dashboards", Name: "dash1", Namespace: "org-2", @@ -164,7 +164,7 @@ func Test_legacyServer_Check(t *testing.T) { req: &authzv1.CheckRequest{ Subject: "user:1", Verb: "get", - Group: "dashboards.grafana.app", + Group: "dashboard.grafana.app", Resource: "dashboards", Name: "dash1", Namespace: "stacks-2", diff --git a/pkg/services/authz/zanzana/server/server_batch_check_test.go b/pkg/services/authz/zanzana/server/server_batch_check_test.go index 9b64be2651f..70c65b8ed44 100644 --- a/pkg/services/authz/zanzana/server/server_batch_check_test.go +++ b/pkg/services/authz/zanzana/server/server_batch_check_test.go @@ -31,7 +31,7 @@ func newBatch(subject, group, resource string, items []*authzextv1.BatchCheckIte } func testBatchCheck(t *testing.T, server *Server) { - t.Run("user:1 should only be able to read resource:dashboards.grafana.app/dashboards/1", func(t *testing.T) { + t.Run("user:1 should only be able to read resource:dashboard.grafana.app/dashboards/1", func(t *testing.T) { groupPrefix := zanzana.FormatGroupResource(dashboardGroup, dashboardResource) res, err := server.BatchCheck(context.Background(), newBatch("user:1", dashboardGroup, dashboardResource, []*authzextv1.BatchCheckItem{ {Name: "1", Folder: "1"}, @@ -44,7 +44,7 @@ func testBatchCheck(t *testing.T, server *Server) { assert.False(t, res.Groups[groupPrefix].Items["2"]) }) - t.Run("user:2 should be able to read resource:dashboards.grafana.app/dashboards/{1,2} through namespace", func(t *testing.T) { + t.Run("user:2 should be able to read resource:dashboard.grafana.app/dashboards/{1,2} through namespace", func(t *testing.T) { groupPrefix := zanzana.FormatGroupResource(dashboardGroup, dashboardResource) res, err := server.BatchCheck(context.Background(), newBatch("user:2", dashboardGroup, dashboardResource, []*authzextv1.BatchCheckItem{ {Name: "1", Folder: "1"}, @@ -54,7 +54,7 @@ func testBatchCheck(t *testing.T, server *Server) { assert.Len(t, res.Groups[groupPrefix].Items, 2) }) - t.Run("user:3 should be able to read resource:dashboards.grafana.app/dashboards/1 with set relation", func(t *testing.T) { + t.Run("user:3 should be able to read resource:dashboard.grafana.app/dashboards/1 with set relation", func(t *testing.T) { groupPrefix := zanzana.FormatGroupResource(dashboardGroup, dashboardResource) res, err := server.BatchCheck(context.Background(), newBatch("user:3", dashboardGroup, dashboardResource, []*authzextv1.BatchCheckItem{ {Name: "1", Folder: "1"}, @@ -67,7 +67,7 @@ func testBatchCheck(t *testing.T, server *Server) { assert.False(t, res.Groups[groupPrefix].Items["2"]) }) - t.Run("user:4 should be able to read all dashboards.grafana.app/dashboards in folder 1 and 3", func(t *testing.T) { + t.Run("user:4 should be able to read all dashboard.grafana.app/dashboards in folder 1 and 3", func(t *testing.T) { groupPrefix := zanzana.FormatGroupResource(dashboardGroup, dashboardResource) res, err := server.BatchCheck(context.Background(), newBatch("user:4", dashboardGroup, dashboardResource, []*authzextv1.BatchCheckItem{ {Name: "1", Folder: "1"}, @@ -82,7 +82,7 @@ func testBatchCheck(t *testing.T, server *Server) { assert.False(t, res.Groups[groupPrefix].Items["3"]) }) - t.Run("user:5 should be able to read resource:dashboards.grafana.app/dashboards/1 through folder with set relation", func(t *testing.T) { + t.Run("user:5 should be able to read resource:dashboard.grafana.app/dashboards/1 through folder with set relation", func(t *testing.T) { groupPrefix := zanzana.FormatGroupResource(dashboardGroup, dashboardResource) res, err := server.BatchCheck(context.Background(), newBatch("user:5", dashboardGroup, dashboardResource, []*authzextv1.BatchCheckItem{ {Name: "1", Folder: "1"}, diff --git a/pkg/services/authz/zanzana/server/server_check_test.go b/pkg/services/authz/zanzana/server/server_check_test.go index 78fa4609dbb..72f80d5cea5 100644 --- a/pkg/services/authz/zanzana/server/server_check_test.go +++ b/pkg/services/authz/zanzana/server/server_check_test.go @@ -24,7 +24,7 @@ func testCheck(t *testing.T, server *Server) { } } - t.Run("user:1 should only be able to read resource:dashboards.grafana.app/dashboards/1", func(t *testing.T) { + t.Run("user:1 should only be able to read resource:dashboard.grafana.app/dashboards/1", func(t *testing.T) { res, err := server.Check(context.Background(), newRead("user:1", dashboardGroup, dashboardResource, "1", "1")) require.NoError(t, err) assert.True(t, res.GetAllowed()) @@ -35,13 +35,13 @@ func testCheck(t *testing.T, server *Server) { assert.False(t, res.GetAllowed()) }) - t.Run("user:2 should be able to read resource:dashboards.grafana.app/dashboards/1 through namespace", func(t *testing.T) { + t.Run("user:2 should be able to read resource:dashboard.grafana.app/dashboards/1 through namespace", func(t *testing.T) { res, err := server.Check(context.Background(), newRead("user:2", dashboardGroup, dashboardResource, "1", "1")) require.NoError(t, err) assert.True(t, res.GetAllowed()) }) - t.Run("user:3 should be able to read resource:dashboards.grafana.app/dashboards/1 with set relation", func(t *testing.T) { + t.Run("user:3 should be able to read resource:dashboard.grafana.app/dashboards/1 with set relation", func(t *testing.T) { res, err := server.Check(context.Background(), newRead("user:3", dashboardGroup, dashboardResource, "1", "1")) require.NoError(t, err) assert.True(t, res.GetAllowed()) @@ -52,7 +52,7 @@ func testCheck(t *testing.T, server *Server) { assert.False(t, res.GetAllowed()) }) - t.Run("user:4 should be able to read all dashboards.grafana.app/dashboards in folder 1 and 3", func(t *testing.T) { + t.Run("user:4 should be able to read all dashboard.grafana.app/dashboards in folder 1 and 3", func(t *testing.T) { res, err := server.Check(context.Background(), newRead("user:4", dashboardGroup, dashboardResource, "1", "1")) require.NoError(t, err) assert.True(t, res.GetAllowed()) @@ -71,7 +71,7 @@ func testCheck(t *testing.T, server *Server) { assert.False(t, res.GetAllowed()) }) - t.Run("user:5 should be able to read resource:dashboards.grafana.app/dashboards/1 through folder with set relation", func(t *testing.T) { + t.Run("user:5 should be able to read resource:dashboard.grafana.app/dashboards/1 through folder with set relation", func(t *testing.T) { res, err := server.Check(context.Background(), newRead("user:5", dashboardGroup, dashboardResource, "1", "1")) require.NoError(t, err) assert.True(t, res.GetAllowed()) diff --git a/pkg/services/authz/zanzana/server/server_list_test.go b/pkg/services/authz/zanzana/server/server_list_test.go index cc7ef17126c..778f286be3e 100644 --- a/pkg/services/authz/zanzana/server/server_list_test.go +++ b/pkg/services/authz/zanzana/server/server_list_test.go @@ -22,7 +22,7 @@ func testList(t *testing.T, server *Server) { } } - t.Run("user:1 should list resource:dashboards.grafana.app/dashboards/1", func(t *testing.T) { + t.Run("user:1 should list resource:dashboard.grafana.app/dashboards/1", func(t *testing.T) { res, err := server.List(context.Background(), newList("user:1", dashboardGroup, dashboardResource)) require.NoError(t, err) assert.Len(t, res.GetItems(), 1) @@ -38,7 +38,7 @@ func testList(t *testing.T, server *Server) { assert.Len(t, res.GetFolders(), 0) }) - t.Run("user:3 should be able to list resource:dashboards.grafana.app/dashboards/1 with set relation", func(t *testing.T) { + t.Run("user:3 should be able to list resource:dashboard.grafana.app/dashboards/1 with set relation", func(t *testing.T) { res, err := server.List(context.Background(), newList("user:3", dashboardGroup, dashboardResource)) require.NoError(t, err) @@ -47,7 +47,7 @@ func testList(t *testing.T, server *Server) { assert.Equal(t, res.GetItems()[0], "1") }) - t.Run("user:4 should be able to list all dashboards.grafana.app/dashboards in folder 1 and 3", func(t *testing.T) { + t.Run("user:4 should be able to list all dashboard.grafana.app/dashboards in folder 1 and 3", func(t *testing.T) { res, err := server.List(context.Background(), newList("user:4", dashboardGroup, dashboardResource)) require.NoError(t, err) assert.Len(t, res.GetItems(), 0) @@ -64,7 +64,7 @@ func testList(t *testing.T, server *Server) { assert.Equal(t, second, "3") }) - t.Run("user:5 should be get list all dashboards.grafana.app/dashboards in folder 1 with set relation", func(t *testing.T) { + t.Run("user:5 should be get list all dashboard.grafana.app/dashboards in folder 1 with set relation", func(t *testing.T) { res, err := server.List(context.Background(), newList("user:5", dashboardGroup, dashboardResource)) require.NoError(t, err) assert.Len(t, res.GetItems(), 0) diff --git a/pkg/storage/unified/client.go b/pkg/storage/unified/client.go index c1cf0a2513b..9cdd69d6a19 100644 --- a/pkg/storage/unified/client.go +++ b/pkg/storage/unified/client.go @@ -17,6 +17,7 @@ import ( "github.com/grafana/grafana/pkg/infra/tracing" "github.com/grafana/grafana/pkg/services/apiserver/options" "github.com/grafana/grafana/pkg/services/authn/grpcutils" + "github.com/grafana/grafana/pkg/services/authz" "github.com/grafana/grafana/pkg/services/featuremgmt" "github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/storage/unified/resource" @@ -32,6 +33,7 @@ func ProvideUnifiedStorageClient( db infraDB.DB, tracer tracing.Tracer, reg prometheus.Registerer, + authzc authz.Client, ) (resource.ResourceClient, error) { // See: apiserver.ApplyGrafanaConfig(cfg, features, o) apiserverCfg := cfg.SectionWithEnvOverrides("grafana-apiserver") @@ -95,7 +97,7 @@ func ProvideUnifiedStorageClient( // Use the local SQL default: - server, err := sql.NewResourceServer(ctx, db, cfg, features, tracer, reg) + server, err := sql.NewResourceServer(ctx, db, cfg, features, tracer, reg, authzc) if err != nil { return nil, err } diff --git a/pkg/storage/unified/resource/access.go b/pkg/storage/unified/resource/access.go index af6f2582d33..97781a7c9d8 100644 --- a/pkg/storage/unified/resource/access.go +++ b/pkg/storage/unified/resource/access.go @@ -2,6 +2,8 @@ package resource import ( "context" + "log/slog" + "time" "github.com/grafana/authlib/authz" "github.com/grafana/authlib/claims" @@ -24,3 +26,82 @@ func (c *staticAuthzClient) Compile(ctx context.Context, id claims.AuthInfo, req } var _ authz.AccessClient = &staticAuthzClient{} + +type groupResource map[string]map[string]interface{} + +// authzLimitedClient is a client that enforces RBAC for the limited number of groups and resources. +// This is a temporary solution until the authz service is fully implemented. +// The authz service will be responsible for enforcing RBAC. +// For now, it makes one call to the authz service for each list items. This is known to be inefficient. +type authzLimitedClient struct { + client authz.AccessChecker + // whitelist is a map of group to resources that are compatible with RBAC. + whitelist groupResource + logger *slog.Logger +} + +// NewAuthzLimitedClient creates a new authzLimitedClient. +func NewAuthzLimitedClient(client authz.AccessChecker) authz.AccessClient { + logger := slog.Default().With("logger", "limited-authz-client") + return &authzLimitedClient{ + client: client, + whitelist: groupResource{ + "dashboard.grafana.app": map[string]interface{}{"dashboards": nil}, + "folder.grafana.app": map[string]interface{}{"folders": nil}, + }, + logger: logger, + } +} + +// Check implements authz.AccessClient. +func (c authzLimitedClient) Check(ctx context.Context, id claims.AuthInfo, req authz.CheckRequest) (authz.CheckResponse, error) { + if !c.IsCompatibleWithRBAC(req.Group, req.Resource) { + c.logger.Debug("Check", "group", req.Group, "resource", req.Resource, "rbac", false, "allowed", true) + return authz.CheckResponse{Allowed: true}, nil + } + t := time.Now() + resp, err := c.client.Check(ctx, id, req) + if err != nil { + c.logger.Error("Check", "group", req.Group, "resource", req.Resource, "rbac", true, "error", err, "duration", time.Since(t)) + return resp, err + } + c.logger.Debug("Check", "group", req.Group, "resource", req.Resource, "rbac", true, "allowed", resp.Allowed, "duration", time.Since(t)) + return resp, nil +} + +// Compile implements authz.AccessClient. +func (c authzLimitedClient) Compile(ctx context.Context, id claims.AuthInfo, req authz.ListRequest) (authz.ItemChecker, error) { + return func(namespace string, name, folder string) bool { + // TODO: Implement For now we perform the check for each item. + if !c.IsCompatibleWithRBAC(req.Group, req.Resource) { + c.logger.Debug("Compile.Check", "group", req.Group, "resource", req.Resource, "namespace", namespace, "name", name, "folder", folder, "rbac", false, "allowed", true) + return true + } + t := time.Now() + r, err := c.client.Check(ctx, id, authz.CheckRequest{ + Verb: "get", + Group: req.Group, + Resource: req.Resource, + Namespace: namespace, + Name: name, + Folder: folder, + }) + if err != nil { + c.logger.Error("Compile.Check", "group", req.Group, "resource", req.Resource, "namespace", namespace, "name", name, "folder", folder, "rbac", true, "error", err, "duration", time.Since(t)) + return false + } + c.logger.Debug("Compile.Check", "group", req.Group, "resource", req.Resource, "namespace", namespace, "name", name, "folder", folder, "rbac", true, "allowed", r.Allowed, "duration", time.Since(t)) + return r.Allowed + }, nil +} + +func (c authzLimitedClient) IsCompatibleWithRBAC(group, resource string) bool { + if _, ok := c.whitelist[group]; ok { + if _, ok := c.whitelist[group][resource]; ok { + return true + } + } + return false +} + +var _ authz.AccessClient = &authzLimitedClient{} diff --git a/pkg/storage/unified/resource/access_test.go b/pkg/storage/unified/resource/access_test.go new file mode 100644 index 00000000000..129dd28f696 --- /dev/null +++ b/pkg/storage/unified/resource/access_test.go @@ -0,0 +1,62 @@ +package resource + +import ( + "context" + "testing" + + "github.com/grafana/authlib/authz" + "github.com/stretchr/testify/assert" +) + +func TestAuthzLimitedClient_Check(t *testing.T) { + mockClient := &staticAuthzClient{allowed: false} + client := NewAuthzLimitedClient(mockClient) + + tests := []struct { + group string + resource string + expected bool + }{ + {"dashboard.grafana.app", "dashboards", false}, + {"folder.grafana.app", "folders", false}, + {"unknown.group", "unknown.resource", true}, + } + + for _, test := range tests { + req := authz.CheckRequest{ + Group: test.group, + Resource: test.resource, + } + resp, err := client.Check(context.Background(), nil, req) + assert.NoError(t, err) + assert.Equal(t, test.expected, resp.Allowed) + } +} + +func TestAuthzLimitedClient_Compile(t *testing.T) { + mockClient := &staticAuthzClient{allowed: false} + client := NewAuthzLimitedClient(mockClient) + + tests := []struct { + group string + resource string + expected bool + }{ + {"dashboard.grafana.app", "dashboards", false}, + {"folder.grafana.app", "folders", false}, + {"unknown.group", "unknown.resource", true}, + } + + for _, test := range tests { + req := authz.ListRequest{ + Group: test.group, + Resource: test.resource, + } + checker, err := client.Compile(context.Background(), nil, req) + assert.NoError(t, err) + assert.NotNil(t, checker) + + result := checker("namespace", "name", "folder") + assert.Equal(t, test.expected, result) + } +} diff --git a/pkg/storage/unified/sql/server.go b/pkg/storage/unified/sql/server.go index e1b730d4744..ddbbd99ec7d 100644 --- a/pkg/storage/unified/sql/server.go +++ b/pkg/storage/unified/sql/server.go @@ -11,6 +11,7 @@ import ( "github.com/grafana/grafana/pkg/apimachinery/identity" infraDB "github.com/grafana/grafana/pkg/infra/db" "github.com/grafana/grafana/pkg/infra/tracing" + "github.com/grafana/grafana/pkg/services/authz" "github.com/grafana/grafana/pkg/services/featuremgmt" "github.com/grafana/grafana/pkg/setting" "github.com/grafana/grafana/pkg/storage/unified/resource" @@ -18,7 +19,7 @@ import ( ) // Creates a new ResourceServer -func NewResourceServer(ctx context.Context, db infraDB.DB, cfg *setting.Cfg, features featuremgmt.FeatureToggles, tracer tracing.Tracer, reg prometheus.Registerer) (resource.ResourceServer, error) { +func NewResourceServer(ctx context.Context, db infraDB.DB, cfg *setting.Cfg, features featuremgmt.FeatureToggles, tracer tracing.Tracer, reg prometheus.Registerer, ac authz.Client) (resource.ResourceServer, error) { apiserverCfg := cfg.SectionWithEnvOverrides("grafana-apiserver") opts := resource.ResourceServerOptions{ Tracer: tracer, @@ -27,7 +28,9 @@ func NewResourceServer(ctx context.Context, db infraDB.DB, cfg *setting.Cfg, fea }, Reg: reg, } - + if ac != nil { + opts.AccessClient = resource.NewAuthzLimitedClient(ac) + } // Support local file blob if strings.HasPrefix(opts.Blob.URL, "./data/") { dir := strings.Replace(opts.Blob.URL, "./data", cfg.DataPath, 1) diff --git a/pkg/storage/unified/sql/service.go b/pkg/storage/unified/sql/service.go index 83105efb371..62cf7612f40 100644 --- a/pkg/storage/unified/sql/service.go +++ b/pkg/storage/unified/sql/service.go @@ -12,6 +12,7 @@ import ( "github.com/grafana/grafana/pkg/infra/tracing" "github.com/grafana/grafana/pkg/modules" "github.com/grafana/grafana/pkg/services/authn/grpcutils" + "github.com/grafana/grafana/pkg/services/authz" "github.com/grafana/grafana/pkg/services/featuremgmt" "github.com/grafana/grafana/pkg/services/grpcserver" "github.com/grafana/grafana/pkg/services/grpcserver/interceptors" @@ -93,7 +94,12 @@ func ProvideUnifiedStorageGrpcService( } func (s *service) start(ctx context.Context) error { - server, err := NewResourceServer(ctx, s.db, s.cfg, s.features, s.tracing, s.reg) + authzClient, err := authz.ProvideStandaloneAuthZClient(s.cfg, s.features, s.tracing) + if err != nil { + return err + } + + server, err := NewResourceServer(ctx, s.db, s.cfg, s.features, s.tracing, s.reg, authzClient) if err != nil { return err } diff --git a/public/app/features/dashboard/api/dashboard_api.ts b/public/app/features/dashboard/api/dashboard_api.ts index 84c6f2e98f3..8d54bb66a0e 100644 --- a/public/app/features/dashboard/api/dashboard_api.ts +++ b/public/app/features/dashboard/api/dashboard_api.ts @@ -50,7 +50,7 @@ interface DashboardWithAccessInfo extends Resource;