diff --git a/pkg/services/authz/zanzana/common/tuple.go b/pkg/services/authz/zanzana/common/tuple.go index 1105e44275a..294427631a8 100644 --- a/pkg/services/authz/zanzana/common/tuple.go +++ b/pkg/services/authz/zanzana/common/tuple.go @@ -6,12 +6,14 @@ import ( openfgav1 "github.com/openfga/api/proto/openfga/v1" "google.golang.org/protobuf/types/known/structpb" + dashboardalpha1 "github.com/grafana/grafana/pkg/apis/dashboard/v0alpha1" authzextv1 "github.com/grafana/grafana/pkg/services/authz/proto/v1" ) const ( TypeUser string = "user" TypeServiceAccount string = "service-account" + TypeRenderService string = "render" TypeTeam string = "team" TypeRole string = "role" TypeFolder string = "folder" @@ -245,3 +247,21 @@ func ToOpenFGATuples(tuples []*authzextv1.Tuple) []*openfgav1.Tuple { } return result } + +func AddRenderContext(req *openfgav1.CheckRequest) { + if req.ContextualTuples == nil { + req.ContextualTuples = &openfgav1.ContextualTupleKeys{} + } + if req.ContextualTuples.TupleKeys == nil { + req.ContextualTuples.TupleKeys = make([]*openfgav1.TupleKey, 0) + } + + req.ContextualTuples.TupleKeys = append(req.ContextualTuples.TupleKeys, &openfgav1.TupleKey{ + User: req.TupleKey.User, + Relation: "view", + Object: NewNamespaceResourceIdent( + dashboardalpha1.DashboardResourceInfo.GroupResource().Group, + dashboardalpha1.DashboardResourceInfo.GroupResource().Resource, + ), + }) +} diff --git a/pkg/services/authz/zanzana/schema/schema_core.fga b/pkg/services/authz/zanzana/schema/schema_core.fga index 5b24adb38a9..86a3a212361 100644 --- a/pkg/services/authz/zanzana/schema/schema_core.fga +++ b/pkg/services/authz/zanzana/schema/schema_core.fga @@ -1,22 +1,24 @@ module core +type user + +type service-account + +type render + type namespace relations - define view: [user, service-account, team#member, role#assignee] or edit + define view: [user, service-account, render, team#member, role#assignee] or edit define edit: [user, service-account, team#member, role#assignee] or admin define admin: [user, service-account, team#member, role#assignee] - define read: [user, service-account, team#member, role#assignee] or view + define read: [user, service-account, render, team#member, role#assignee] or view define create: [user, service-account, team#member, role#assignee] or edit define write: [user, service-account, team#member, role#assignee] or edit define delete: [user, service-account, team#member, role#assignee] or edit define permissions_read: [user, service-account, team#member, role#assignee] or admin define permissions_write: [user, service-account, team#member, role#assignee] or admin -type user - -type service-account - type role relations define assignee: [user, service-account, team#member, role#assignee] diff --git a/pkg/services/authz/zanzana/server/server_check.go b/pkg/services/authz/zanzana/server/server_check.go index 9c4de93eb68..792a9561760 100644 --- a/pkg/services/authz/zanzana/server/server_check.go +++ b/pkg/services/authz/zanzana/server/server_check.go @@ -2,6 +2,8 @@ package server import ( "context" + "fmt" + "strings" authzv1 "github.com/grafana/authlib/authz/proto/v1" openfgav1 "github.com/openfga/api/proto/openfga/v1" @@ -40,7 +42,7 @@ func (s *Server) Check(ctx context.Context, r *authzv1.CheckRequest) (*authzv1.C // checkTyped performes check on the root "namespace". If subject has access through the namespace they have access to // every resource for that "GroupResource". func (s *Server) checkNamespace(ctx context.Context, subject, relation, group, resource string, store *storeInfo) (*authzv1.CheckResponse, error) { - res, err := s.openfga.Check(ctx, &openfgav1.CheckRequest{ + req := &openfgav1.CheckRequest{ StoreId: store.ID, AuthorizationModelId: store.ModelID, TupleKey: &openfgav1.CheckRequestTupleKey{ @@ -48,7 +50,12 @@ func (s *Server) checkNamespace(ctx context.Context, subject, relation, group, r Relation: relation, Object: common.NewNamespaceResourceIdent(group, resource), }, - }) + } + if strings.HasPrefix(subject, fmt.Sprintf("%s:", common.TypeRenderService)) { + common.AddRenderContext(req) + } + + res, err := s.openfga.Check(ctx, req) if err != nil { return nil, err } diff --git a/pkg/services/authz/zanzana/zanzana.go b/pkg/services/authz/zanzana/zanzana.go index b7af6983eda..512f1e6a07f 100644 --- a/pkg/services/authz/zanzana/zanzana.go +++ b/pkg/services/authz/zanzana/zanzana.go @@ -13,6 +13,7 @@ import ( const ( TypeUser = common.TypeUser TypeServiceAccount = common.TypeServiceAccount + TypeRenderService = common.TypeRenderService TypeTeam = common.TypeTeam TypeRole = common.TypeRole TypeFolder = common.TypeFolder