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:
@@ -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
|
||||
}
|
||||
|
||||
@@ -58,6 +58,8 @@ const (
|
||||
KindFolders string = "folders"
|
||||
)
|
||||
|
||||
var ClusterNamespace = common.ClusterNamespace
|
||||
|
||||
var (
|
||||
ToAuthzExtTupleKey = common.ToAuthzExtTupleKey
|
||||
ToAuthzExtTupleKeys = common.ToAuthzExtTupleKeys
|
||||
|
||||
Reference in New Issue
Block a user