Zanzana: Refactor fixed roles (use global store) (#97884)

* Zanzana: Pass contextual tuples for authorization

* global reconciler for fixed roles

* inject tuples from global store

* fix adding contextual tuples

* cleanup

* don't error on auth context fail

* add todo

* add context for List

* add caching

* remove unused

* use constant for global namespace

* Rename global namespace to cluster namespace
This commit is contained in:
Alexander Zobnin
2025-01-07 13:49:55 +01:00
committed by GitHub
parent 9558a25ebe
commit 7e5cb7d8d6
9 changed files with 298 additions and 12 deletions
@@ -82,6 +82,8 @@ var RelationsFolder = append(
RelationDelete,
)
const ClusterNamespace = "cluster"
func IsGroupResourceRelation(relation string) bool {
return isValidRelation(relation, RelationsGroupResource)
}
@@ -259,6 +261,14 @@ func ToOpenFGATupleKey(t *authzextv1.TupleKey) *openfgav1.TupleKey {
return tupleKey
}
func ToOpenFGATupleKeys(tuples []*authzextv1.TupleKey) []*openfgav1.TupleKey {
result := make([]*openfgav1.TupleKey, 0, len(tuples))
for _, t := range tuples {
result = append(result, ToOpenFGATupleKey(t))
}
return result
}
func ToOpenFGATupleKeyWithoutCondition(t *authzextv1.TupleKeyWithoutCondition) *openfgav1.TupleKeyWithoutCondition {
return &openfgav1.TupleKeyWithoutCondition{
User: t.GetUser(),
@@ -1,6 +1,7 @@
package server
import (
"context"
"sync"
"time"
@@ -13,6 +14,7 @@ import (
"github.com/grafana/grafana/pkg/infra/localcache"
"github.com/grafana/grafana/pkg/infra/log"
authzextv1 "github.com/grafana/grafana/pkg/services/authz/proto/v1"
"github.com/grafana/grafana/pkg/services/authz/zanzana/common"
"github.com/grafana/grafana/pkg/setting"
)
@@ -91,3 +93,71 @@ func NewAuthz(cfg *setting.Cfg, openfga openfgav1.OpenFGAServiceServer, opts ...
return s, nil
}
func (s *Server) getGlobalAuthorizationContext(ctx context.Context) ([]*openfgav1.TupleKey, error) {
cacheKey := "global_authorization_context"
contextualTuples := make([]*openfgav1.TupleKey, 0)
cached, found := s.cache.Get(cacheKey)
if found {
contextualTuples = cached.([]*openfgav1.TupleKey)
return contextualTuples, nil
}
res, err := s.Read(ctx, &authzextv1.ReadRequest{
Namespace: common.ClusterNamespace,
})
if err != nil {
return nil, err
}
tuples := common.ToOpenFGATuples(res.Tuples)
for _, t := range tuples {
contextualTuples = append(contextualTuples, t.GetKey())
}
s.cache.SetDefault(cacheKey, contextualTuples)
return contextualTuples, nil
}
func (s *Server) addCheckAuthorizationContext(ctx context.Context, req *openfgav1.CheckRequest) error {
contextualTuples, err := s.getGlobalAuthorizationContext(ctx)
if err != nil {
return err
}
if len(contextualTuples) == 0 {
return nil
}
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, contextualTuples...)
return nil
}
func (s *Server) addListAuthorizationContext(ctx context.Context, req *openfgav1.ListObjectsRequest) error {
contextualTuples, err := s.getGlobalAuthorizationContext(ctx)
if err != nil {
return err
}
if len(contextualTuples) == 0 {
return nil
}
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, contextualTuples...)
return nil
}
@@ -57,7 +57,7 @@ func (s *Server) checkGroupResource(ctx context.Context, subject, relation, grou
common.AddRenderContext(req)
}
res, err := s.openfga.Check(ctx, req)
res, err := s.check(ctx, req)
if err != nil {
return nil, err
}
@@ -72,7 +72,7 @@ func (s *Server) checkTyped(ctx context.Context, subject, relation, name string,
}
// Check if subject has direct access to resource
res, err := s.openfga.Check(ctx, &openfgav1.CheckRequest{
res, err := s.check(ctx, &openfgav1.CheckRequest{
StoreId: store.ID,
AuthorizationModelId: store.ModelID,
TupleKey: &openfgav1.CheckRequestTupleKey{
@@ -103,7 +103,7 @@ func (s *Server) checkGeneric(ctx context.Context, subject, relation, group, res
if folder != "" && common.IsFolderResourceRelation(folderRelation) {
// Check if subject has access as a sub resource for the folder
res, err := s.openfga.Check(ctx, &openfgav1.CheckRequest{
res, err := s.check(ctx, &openfgav1.CheckRequest{
StoreId: store.ID,
AuthorizationModelId: store.ModelID,
TupleKey: &openfgav1.CheckRequestTupleKey{
@@ -128,7 +128,7 @@ func (s *Server) checkGeneric(ctx context.Context, subject, relation, group, res
}
// Check if subject has direct access to resource
res, err := s.openfga.Check(ctx, &openfgav1.CheckRequest{
res, err := s.check(ctx, &openfgav1.CheckRequest{
StoreId: store.ID,
AuthorizationModelId: store.ModelID,
TupleKey: &openfgav1.CheckRequestTupleKey{
@@ -145,3 +145,12 @@ func (s *Server) checkGeneric(ctx context.Context, subject, relation, group, res
return &authzv1.CheckResponse{Allowed: res.GetAllowed()}, nil
}
func (s *Server) check(ctx context.Context, req *openfgav1.CheckRequest) (*openfgav1.CheckResponse, error) {
err := s.addCheckAuthorizationContext(ctx, req)
if err != nil {
s.logger.Error("failed to add authorization context", "error", err)
}
return s.openfga.Check(ctx, req)
}
@@ -39,6 +39,11 @@ func (s *Server) List(ctx context.Context, r *authzv1.ListRequest) (*authzv1.Lis
}
func (s *Server) listObjects(ctx context.Context, req *openfgav1.ListObjectsRequest) (*openfgav1.ListObjectsResponse, error) {
err := s.addListAuthorizationContext(ctx, req)
if err != nil {
s.logger.Error("failed to add authorization context", "error", err)
}
if s.cfg.UseStreamedListObjects {
return s.streamedListObjects(ctx, req)
}
@@ -18,16 +18,21 @@ func (s *Server) Read(ctx context.Context, req *authzextv1.ReadRequest) (*authze
return nil, err
}
res, err := s.openfga.Read(ctx, &openfgav1.ReadRequest{
StoreId: storeInf.ID,
TupleKey: &openfgav1.ReadRequestTupleKey{
readReq := &openfgav1.ReadRequest{
StoreId: storeInf.ID,
PageSize: req.GetPageSize(),
ContinuationToken: req.GetContinuationToken(),
}
if req.TupleKey != nil {
readReq.TupleKey = &openfgav1.ReadRequestTupleKey{
User: req.GetTupleKey().GetUser(),
Relation: req.GetTupleKey().GetRelation(),
Object: req.GetTupleKey().GetObject(),
},
PageSize: req.GetPageSize(),
ContinuationToken: req.GetContinuationToken(),
})
}
}
res, err := s.openfga.Read(ctx, readReq)
if err != nil {
return nil, err
}
+2
View File
@@ -58,6 +58,8 @@ const (
KindFolders string = "folders"
)
var ClusterNamespace = common.ClusterNamespace
var (
ToAuthzExtTupleKey = common.ToAuthzExtTupleKey
ToAuthzExtTupleKeys = common.ToAuthzExtTupleKeys