Zanzana: Consistently add context (#98862)
* Zanzana: Reworks how contextuals are loaded * Cleanup listObjectWithStream * Run list test with streaming enabled
This commit is contained in:
@@ -27,27 +27,54 @@ func getTypeInfo(group, resource string) (typeInfo, bool) {
|
||||
}
|
||||
|
||||
func NewResourceInfoFromCheck(r *authzv1.CheckRequest) ResourceInfo {
|
||||
if info, ok := getTypeInfo(r.GetGroup(), r.GetResource()); ok {
|
||||
return newResource(info.Type, r.GetGroup(), r.GetResource(), r.GetName(), r.GetFolder(), r.GetSubresource(), info.Relations)
|
||||
}
|
||||
return newResource(TypeResource, r.GetGroup(), r.GetResource(), r.GetName(), r.GetFolder(), r.GetSubresource(), RelationsResource)
|
||||
typ, relations := getTypeAndRelations(r.GetGroup(), r.GetResource())
|
||||
return newResource(
|
||||
typ,
|
||||
r.GetGroup(),
|
||||
r.GetResource(),
|
||||
r.GetName(),
|
||||
r.GetFolder(),
|
||||
r.GetSubresource(),
|
||||
relations,
|
||||
)
|
||||
}
|
||||
|
||||
func NewResourceInfoFromBatchItem(i *authzextv1.BatchCheckItem) ResourceInfo {
|
||||
if info, ok := getTypeInfo(i.GetGroup(), i.GetResource()); ok {
|
||||
return newResource(info.Type, i.GetGroup(), i.GetResource(), i.GetName(), i.GetFolder(), i.GetSubresource(), info.Relations)
|
||||
}
|
||||
return newResource(TypeResource, i.GetGroup(), i.GetResource(), i.GetName(), i.GetFolder(), i.GetSubresource(), RelationsResource)
|
||||
typ, relations := getTypeAndRelations(i.GetGroup(), i.GetResource())
|
||||
return newResource(
|
||||
typ,
|
||||
i.GetGroup(),
|
||||
i.GetResource(),
|
||||
i.GetName(),
|
||||
i.GetFolder(),
|
||||
i.GetSubresource(),
|
||||
relations,
|
||||
)
|
||||
}
|
||||
|
||||
func NewResourceInfoFromList(r *authzv1.ListRequest) ResourceInfo {
|
||||
if info, ok := getTypeInfo(r.GetGroup(), r.GetResource()); ok {
|
||||
return newResource(info.Type, r.GetGroup(), r.GetResource(), "", "", r.GetSubresource(), info.Relations)
|
||||
}
|
||||
return newResource(TypeResource, r.GetGroup(), r.GetResource(), "", "", r.GetSubresource(), RelationsResource)
|
||||
typ, relations := getTypeAndRelations(r.GetGroup(), r.GetResource())
|
||||
return newResource(
|
||||
typ,
|
||||
r.GetGroup(),
|
||||
r.GetResource(),
|
||||
"",
|
||||
"",
|
||||
r.GetSubresource(),
|
||||
relations,
|
||||
)
|
||||
}
|
||||
|
||||
func newResource(typ string, group, resource, name, folder, subresource string, relations []string) ResourceInfo {
|
||||
func getTypeAndRelations(group, resource string) (string, []string) {
|
||||
if info, ok := getTypeInfo(group, resource); ok {
|
||||
return info.Type, info.Relations
|
||||
}
|
||||
return TypeResource, RelationsResource
|
||||
}
|
||||
|
||||
func newResource(
|
||||
typ, group, resource, name, folder, subresource string, relations []string,
|
||||
) ResourceInfo {
|
||||
return ResourceInfo{
|
||||
typ: typ,
|
||||
group: group,
|
||||
|
||||
@@ -2,6 +2,7 @@ package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@@ -11,6 +12,7 @@ import (
|
||||
"github.com/openfga/language/pkg/go/transformer"
|
||||
"go.opentelemetry.io/otel"
|
||||
|
||||
dashboardalpha1 "github.com/grafana/grafana/pkg/apis/dashboard/v2alpha1"
|
||||
"github.com/grafana/grafana/pkg/infra/localcache"
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
authzextv1 "github.com/grafana/grafana/pkg/services/authz/proto/v1"
|
||||
@@ -94,14 +96,39 @@ 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)
|
||||
func (s *Server) getContextuals(ctx context.Context, subject string) (*openfgav1.ContextualTupleKeys, error) {
|
||||
contextuals, err := s.getGlobalAuthorizationContext(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if strings.HasPrefix(subject, common.TypeRenderService+":") {
|
||||
contextuals = append(
|
||||
contextuals,
|
||||
&openfgav1.TupleKey{
|
||||
User: subject,
|
||||
Relation: common.RelationSetView,
|
||||
Object: common.NewGroupResourceIdent(
|
||||
dashboardalpha1.DashboardResourceInfo.GroupResource().Group,
|
||||
dashboardalpha1.DashboardResourceInfo.GroupResource().Resource,
|
||||
"",
|
||||
),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
if len(contextuals) > 0 {
|
||||
return &openfgav1.ContextualTupleKeys{TupleKeys: contextuals}, nil
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (s *Server) getGlobalAuthorizationContext(ctx context.Context) ([]*openfgav1.TupleKey, error) {
|
||||
const cacheKey = "global_authorization_context"
|
||||
cached, found := s.cache.Get(cacheKey)
|
||||
if found {
|
||||
contextualTuples = cached.([]*openfgav1.TupleKey)
|
||||
return contextualTuples, nil
|
||||
return cached.([]*openfgav1.TupleKey), nil
|
||||
}
|
||||
|
||||
res, err := s.Read(ctx, &authzextv1.ReadRequest{
|
||||
@@ -111,53 +138,12 @@ func (s *Server) getGlobalAuthorizationContext(ctx context.Context) ([]*openfgav
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tuples := common.ToOpenFGATuples(res.Tuples)
|
||||
contextualTuples := make([]*openfgav1.TupleKey, 0, len(res.GetTuples()))
|
||||
tuples := common.ToOpenFGATuples(res.GetTuples())
|
||||
for _, t := range tuples {
|
||||
contextualTuples = append(contextualTuples, t.GetKey())
|
||||
}
|
||||
s.cache.SetDefault(cacheKey, contextualTuples)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
@@ -4,13 +4,14 @@ import (
|
||||
"context"
|
||||
|
||||
authzv1 "github.com/grafana/authlib/authz/proto/v1"
|
||||
openfgav1 "github.com/openfga/api/proto/openfga/v1"
|
||||
|
||||
authzextv1 "github.com/grafana/grafana/pkg/services/authz/proto/v1"
|
||||
"github.com/grafana/grafana/pkg/services/authz/zanzana/common"
|
||||
)
|
||||
|
||||
func (s *Server) BatchCheck(ctx context.Context, r *authzextv1.BatchCheckRequest) (*authzextv1.BatchCheckResponse, error) {
|
||||
ctx, span := tracer.Start(ctx, "authzServer.BatchCheck")
|
||||
ctx, span := tracer.Start(ctx, "server.BatchCheck")
|
||||
defer span.End()
|
||||
|
||||
if err := authorize(ctx, r.GetNamespace()); err != nil {
|
||||
@@ -26,10 +27,15 @@ func (s *Server) BatchCheck(ctx context.Context, r *authzextv1.BatchCheckRequest
|
||||
return nil, err
|
||||
}
|
||||
|
||||
contextuals, err := s.getContextuals(ctx, r.GetSubject())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
groupResourceAccess := make(map[string]bool)
|
||||
|
||||
for _, item := range r.GetItems() {
|
||||
res, err := s.batchCheckItem(ctx, r, item, store, groupResourceAccess)
|
||||
res, err := s.batchCheckItem(ctx, r, item, contextuals, store, groupResourceAccess)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -50,6 +56,7 @@ func (s *Server) batchCheckItem(
|
||||
ctx context.Context,
|
||||
r *authzextv1.BatchCheckRequest,
|
||||
item *authzextv1.BatchCheckItem,
|
||||
contextuals *openfgav1.ContextualTupleKeys,
|
||||
store *storeInfo,
|
||||
groupResourceAccess map[string]bool,
|
||||
) (*authzv1.CheckResponse, error) {
|
||||
@@ -61,7 +68,7 @@ func (s *Server) batchCheckItem(
|
||||
|
||||
allowed, ok := groupResourceAccess[groupResource]
|
||||
if !ok {
|
||||
res, err := s.checkGroupResource(ctx, r.GetSubject(), relation, resource, store)
|
||||
res, err := s.checkGroupResource(ctx, r.GetSubject(), relation, resource, contextuals, store)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -75,8 +82,8 @@ func (s *Server) batchCheckItem(
|
||||
}
|
||||
|
||||
if resource.IsGeneric() {
|
||||
return s.checkGeneric(ctx, r.GetSubject(), relation, resource, store)
|
||||
return s.checkGeneric(ctx, r.GetSubject(), relation, resource, contextuals, store)
|
||||
}
|
||||
|
||||
return s.checkTyped(ctx, r.GetSubject(), relation, resource, store)
|
||||
return s.checkTyped(ctx, r.GetSubject(), relation, resource, contextuals, store)
|
||||
}
|
||||
|
||||
@@ -2,8 +2,6 @@ package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
authzv1 "github.com/grafana/authlib/authz/proto/v1"
|
||||
openfgav1 "github.com/openfga/api/proto/openfga/v1"
|
||||
@@ -12,7 +10,7 @@ import (
|
||||
)
|
||||
|
||||
func (s *Server) Check(ctx context.Context, r *authzv1.CheckRequest) (*authzv1.CheckResponse, error) {
|
||||
ctx, span := tracer.Start(ctx, "authzServer.Check")
|
||||
ctx, span := tracer.Start(ctx, "server.Check")
|
||||
defer span.End()
|
||||
|
||||
if err := authorize(ctx, r.GetNamespace()); err != nil {
|
||||
@@ -26,8 +24,13 @@ func (s *Server) Check(ctx context.Context, r *authzv1.CheckRequest) (*authzv1.C
|
||||
|
||||
relation := common.VerbMapping[r.GetVerb()]
|
||||
|
||||
contextuals, err := s.getContextuals(ctx, r.GetSubject())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resource := common.NewResourceInfoFromCheck(r)
|
||||
res, err := s.checkGroupResource(ctx, r.GetSubject(), relation, resource, store)
|
||||
res, err := s.checkGroupResource(ctx, r.GetSubject(), relation, resource, contextuals, store)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -37,20 +40,20 @@ func (s *Server) Check(ctx context.Context, r *authzv1.CheckRequest) (*authzv1.C
|
||||
}
|
||||
|
||||
if resource.IsGeneric() {
|
||||
return s.checkGeneric(ctx, r.GetSubject(), relation, resource, store)
|
||||
return s.checkGeneric(ctx, r.GetSubject(), relation, resource, contextuals, store)
|
||||
}
|
||||
|
||||
return s.checkTyped(ctx, r.GetSubject(), relation, resource, store)
|
||||
return s.checkTyped(ctx, r.GetSubject(), relation, resource, contextuals, store)
|
||||
}
|
||||
|
||||
// checkGroupResource check if subject has access to the full "GroupResource", if they do they can access every object
|
||||
// within it.
|
||||
func (s *Server) checkGroupResource(ctx context.Context, subject, relation string, resource common.ResourceInfo, store *storeInfo) (*authzv1.CheckResponse, error) {
|
||||
func (s *Server) checkGroupResource(ctx context.Context, subject, relation string, resource common.ResourceInfo, contextuals *openfgav1.ContextualTupleKeys, store *storeInfo) (*authzv1.CheckResponse, error) {
|
||||
if !common.IsGroupResourceRelation(relation) {
|
||||
return &authzv1.CheckResponse{Allowed: false}, nil
|
||||
}
|
||||
|
||||
req := &openfgav1.CheckRequest{
|
||||
res, err := s.openfga.Check(ctx, &openfgav1.CheckRequest{
|
||||
StoreId: store.ID,
|
||||
AuthorizationModelId: store.ModelID,
|
||||
TupleKey: &openfgav1.CheckRequestTupleKey{
|
||||
@@ -58,13 +61,8 @@ func (s *Server) checkGroupResource(ctx context.Context, subject, relation strin
|
||||
Relation: relation,
|
||||
Object: resource.GroupResourceIdent(),
|
||||
},
|
||||
}
|
||||
|
||||
if strings.HasPrefix(subject, fmt.Sprintf("%s:", common.TypeRenderService)) {
|
||||
common.AddRenderContext(req)
|
||||
}
|
||||
|
||||
res, err := s.check(ctx, req)
|
||||
ContextualTuples: contextuals,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -73,13 +71,13 @@ func (s *Server) checkGroupResource(ctx context.Context, subject, relation strin
|
||||
}
|
||||
|
||||
// checkTyped checks on our typed resources e.g. folder.
|
||||
func (s *Server) checkTyped(ctx context.Context, subject, relation string, resource common.ResourceInfo, store *storeInfo) (*authzv1.CheckResponse, error) {
|
||||
func (s *Server) checkTyped(ctx context.Context, subject, relation string, resource common.ResourceInfo, contextuals *openfgav1.ContextualTupleKeys, store *storeInfo) (*authzv1.CheckResponse, error) {
|
||||
if !resource.IsValidRelation(relation) {
|
||||
return &authzv1.CheckResponse{Allowed: false}, nil
|
||||
}
|
||||
|
||||
// Check if subject has direct access to resource
|
||||
res, err := s.check(ctx, &openfgav1.CheckRequest{
|
||||
res, err := s.openfga.Check(ctx, &openfgav1.CheckRequest{
|
||||
StoreId: store.ID,
|
||||
AuthorizationModelId: store.ModelID,
|
||||
TupleKey: &openfgav1.CheckRequestTupleKey{
|
||||
@@ -87,6 +85,7 @@ func (s *Server) checkTyped(ctx context.Context, subject, relation string, resou
|
||||
Relation: relation,
|
||||
Object: resource.ResourceIdent(),
|
||||
},
|
||||
ContextualTuples: contextuals,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -102,7 +101,7 @@ func (s *Server) checkTyped(ctx context.Context, subject, relation string, resou
|
||||
// checkGeneric check our generic "resource" type. It checks:
|
||||
// 1. If subject has access as a sub resource for a folder.
|
||||
// 2. If subject has direct access to resource.
|
||||
func (s *Server) checkGeneric(ctx context.Context, subject, relation string, resource common.ResourceInfo, store *storeInfo) (*authzv1.CheckResponse, error) {
|
||||
func (s *Server) checkGeneric(ctx context.Context, subject, relation string, resource common.ResourceInfo, contextuals *openfgav1.ContextualTupleKeys, store *storeInfo) (*authzv1.CheckResponse, error) {
|
||||
var (
|
||||
folderIdent = resource.FolderIdent()
|
||||
resourceCtx = resource.Context()
|
||||
@@ -111,7 +110,7 @@ func (s *Server) checkGeneric(ctx context.Context, subject, relation string, res
|
||||
|
||||
if folderIdent != "" && common.IsFolderResourceRelation(folderRelation) {
|
||||
// Check if subject has access as a sub resource for the folder
|
||||
res, err := s.check(ctx, &openfgav1.CheckRequest{
|
||||
res, err := s.openfga.Check(ctx, &openfgav1.CheckRequest{
|
||||
StoreId: store.ID,
|
||||
AuthorizationModelId: store.ModelID,
|
||||
TupleKey: &openfgav1.CheckRequestTupleKey{
|
||||
@@ -119,7 +118,8 @@ func (s *Server) checkGeneric(ctx context.Context, subject, relation string, res
|
||||
Relation: folderRelation,
|
||||
Object: folderIdent,
|
||||
},
|
||||
Context: resourceCtx,
|
||||
Context: resourceCtx,
|
||||
ContextualTuples: contextuals,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
@@ -137,7 +137,7 @@ func (s *Server) checkGeneric(ctx context.Context, subject, relation string, res
|
||||
}
|
||||
|
||||
// Check if subject has direct access to resource
|
||||
res, err := s.check(ctx, &openfgav1.CheckRequest{
|
||||
res, err := s.openfga.Check(ctx, &openfgav1.CheckRequest{
|
||||
StoreId: store.ID,
|
||||
AuthorizationModelId: store.ModelID,
|
||||
TupleKey: &openfgav1.CheckRequestTupleKey{
|
||||
@@ -145,7 +145,8 @@ func (s *Server) checkGeneric(ctx context.Context, subject, relation string, res
|
||||
Relation: relation,
|
||||
Object: resourceIdent,
|
||||
},
|
||||
Context: resourceCtx,
|
||||
Context: resourceCtx,
|
||||
ContextualTuples: contextuals,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
@@ -154,12 +155,3 @@ func (s *Server) checkGeneric(ctx context.Context, subject, relation string, 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)
|
||||
}
|
||||
|
||||
@@ -12,14 +12,19 @@ import (
|
||||
)
|
||||
|
||||
func (s *Server) List(ctx context.Context, r *authzv1.ListRequest) (*authzv1.ListResponse, error) {
|
||||
ctx, span := tracer.Start(ctx, "authzServer.List")
|
||||
ctx, span := tracer.Start(ctx, "server.List")
|
||||
defer span.End()
|
||||
|
||||
if err := authorize(ctx, r.GetNamespace()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
store, err := s.getStoreInfo(ctx, r.Namespace)
|
||||
store, err := s.getStoreInfo(ctx, r.GetNamespace())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
contextuals, err := s.getContextuals(ctx, r.GetSubject())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -27,7 +32,7 @@ func (s *Server) List(ctx context.Context, r *authzv1.ListRequest) (*authzv1.Lis
|
||||
relation := common.VerbMapping[r.GetVerb()]
|
||||
resource := common.NewResourceInfoFromList(r)
|
||||
|
||||
res, err := s.checkGroupResource(ctx, r.GetSubject(), relation, resource, store)
|
||||
res, err := s.checkGroupResource(ctx, r.GetSubject(), relation, resource, contextuals, store)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -37,25 +42,20 @@ func (s *Server) List(ctx context.Context, r *authzv1.ListRequest) (*authzv1.Lis
|
||||
}
|
||||
|
||||
if resource.IsGeneric() {
|
||||
return s.listGeneric(ctx, r.GetSubject(), relation, resource, store)
|
||||
return s.listGeneric(ctx, r.GetSubject(), relation, resource, contextuals, store)
|
||||
}
|
||||
|
||||
return s.listTyped(ctx, r.GetSubject(), relation, resource, store)
|
||||
return s.listTyped(ctx, r.GetSubject(), relation, resource, contextuals, store)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
return s.openfga.ListObjects(ctx, req)
|
||||
}
|
||||
|
||||
func (s *Server) listTyped(ctx context.Context, subject, relation string, resource common.ResourceInfo, store *storeInfo) (*authzv1.ListResponse, error) {
|
||||
func (s *Server) listTyped(ctx context.Context, subject, relation string, resource common.ResourceInfo, contextuals *openfgav1.ContextualTupleKeys, store *storeInfo) (*authzv1.ListResponse, error) {
|
||||
if !resource.IsValidRelation(relation) {
|
||||
return &authzv1.ListResponse{}, nil
|
||||
}
|
||||
@@ -67,6 +67,7 @@ func (s *Server) listTyped(ctx context.Context, subject, relation string, resour
|
||||
Type: resource.Type(),
|
||||
Relation: relation,
|
||||
User: subject,
|
||||
ContextualTuples: contextuals,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -77,7 +78,7 @@ func (s *Server) listTyped(ctx context.Context, subject, relation string, resour
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) listGeneric(ctx context.Context, subject, relation string, resource common.ResourceInfo, store *storeInfo) (*authzv1.ListResponse, error) {
|
||||
func (s *Server) listGeneric(ctx context.Context, subject, relation string, resource common.ResourceInfo, contextuals *openfgav1.ContextualTupleKeys, store *storeInfo) (*authzv1.ListResponse, error) {
|
||||
var (
|
||||
folderRelation = common.FolderResourceRelation(relation)
|
||||
resourceCtx = resource.Context()
|
||||
@@ -93,6 +94,7 @@ func (s *Server) listGeneric(ctx context.Context, subject, relation string, reso
|
||||
Relation: folderRelation,
|
||||
User: subject,
|
||||
Context: resourceCtx,
|
||||
ContextualTuples: contextuals,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
@@ -112,6 +114,7 @@ func (s *Server) listGeneric(ctx context.Context, subject, relation string, reso
|
||||
Relation: relation,
|
||||
User: subject,
|
||||
Context: resourceCtx,
|
||||
ContextualTuples: contextuals,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@@ -14,11 +14,11 @@ func (s *Server) streamedListObjects(ctx context.Context, req *openfgav1.ListObj
|
||||
if !s.cfg.CheckQueryCache {
|
||||
return s.listObjectsWithStream(ctx, req)
|
||||
}
|
||||
return s.streamedListObjectsCached(ctx, req)
|
||||
return s.listObjectsWithStreamCached(ctx, req)
|
||||
}
|
||||
|
||||
func (s *Server) streamedListObjectsCached(ctx context.Context, req *openfgav1.ListObjectsRequest) (*openfgav1.ListObjectsResponse, error) {
|
||||
ctx, span := tracer.Start(ctx, "authzServer.streamedListObjectsCached")
|
||||
func (s *Server) listObjectsWithStreamCached(ctx context.Context, req *openfgav1.ListObjectsRequest) (*openfgav1.ListObjectsResponse, error) {
|
||||
ctx, span := tracer.Start(ctx, "server.listObjectsWithStreamCached")
|
||||
defer span.End()
|
||||
|
||||
reqHash, err := getRequestHash(req)
|
||||
@@ -39,7 +39,7 @@ func (s *Server) streamedListObjectsCached(ctx context.Context, req *openfgav1.L
|
||||
}
|
||||
|
||||
func (s *Server) listObjectsWithStream(ctx context.Context, req *openfgav1.ListObjectsRequest) (*openfgav1.ListObjectsResponse, error) {
|
||||
ctx, span := tracer.Start(ctx, "authzServer.listObjectsWithStream")
|
||||
ctx, span := tracer.Start(ctx, "server.listObjectsWithStream")
|
||||
defer span.End()
|
||||
|
||||
r := &openfgav1.StreamedListObjectsRequest{
|
||||
@@ -49,47 +49,32 @@ func (s *Server) listObjectsWithStream(ctx context.Context, req *openfgav1.ListO
|
||||
Relation: req.GetRelation(),
|
||||
User: req.GetUser(),
|
||||
Context: req.GetContext(),
|
||||
ContextualTuples: req.ContextualTuples,
|
||||
}
|
||||
|
||||
clientStream, err := s.openfgaClient.StreamedListObjects(ctx, r)
|
||||
stream, err := s.openfgaClient.StreamedListObjects(ctx, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
done := make(chan struct{})
|
||||
var streamedObjectIDs []string
|
||||
var streamingErr error
|
||||
var streamingResp *openfgav1.StreamedListObjectsResponse
|
||||
go func() {
|
||||
for {
|
||||
streamingResp, streamingErr = clientStream.Recv()
|
||||
if streamingErr == nil {
|
||||
streamedObjectIDs = append(streamedObjectIDs, streamingResp.GetObject())
|
||||
} else {
|
||||
if errors.Is(streamingErr, io.EOF) {
|
||||
streamingErr = nil
|
||||
}
|
||||
var objects []string
|
||||
for {
|
||||
res, err := stream.Recv()
|
||||
if err != nil {
|
||||
if errors.Is(err, io.EOF) {
|
||||
break
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
done <- struct{}{}
|
||||
}()
|
||||
<-done
|
||||
|
||||
if streamingErr != nil {
|
||||
return nil, streamingErr
|
||||
objects = append(objects, res.GetObject())
|
||||
}
|
||||
|
||||
return &openfgav1.ListObjectsResponse{
|
||||
Objects: streamedObjectIDs,
|
||||
Objects: objects,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func getRequestHash(req *openfgav1.ListObjectsRequest) (string, error) {
|
||||
if req == nil {
|
||||
return "", errors.New("request must not be empty")
|
||||
}
|
||||
|
||||
hash := fnv.New64a()
|
||||
_, err := hash.Write([]byte(req.String()))
|
||||
if err != nil {
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
)
|
||||
|
||||
func (s *Server) Read(ctx context.Context, req *authzextv1.ReadRequest) (*authzextv1.ReadResponse, error) {
|
||||
ctx, span := tracer.Start(ctx, "authzServer.Read")
|
||||
ctx, span := tracer.Start(ctx, "server.Read")
|
||||
defer span.End()
|
||||
|
||||
if err := authorize(ctx, req.GetNamespace()); err != nil {
|
||||
|
||||
@@ -56,6 +56,12 @@ func TestIntegrationServer(t *testing.T) {
|
||||
testList(t, srv)
|
||||
})
|
||||
|
||||
t.Run("test list streaming", func(t *testing.T) {
|
||||
srv.cfg.UseStreamedListObjects = true
|
||||
testList(t, srv)
|
||||
srv.cfg.UseStreamedListObjects = false
|
||||
})
|
||||
|
||||
t.Run("test batch check", func(t *testing.T) {
|
||||
testBatchCheck(t, srv)
|
||||
})
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
)
|
||||
|
||||
func (s *Server) Write(ctx context.Context, req *authzextv1.WriteRequest) (*authzextv1.WriteResponse, error) {
|
||||
ctx, span := tracer.Start(ctx, "authzServer.Write")
|
||||
ctx, span := tracer.Start(ctx, "server.Write")
|
||||
defer span.End()
|
||||
|
||||
if err := authorize(ctx, req.GetNamespace()); err != nil {
|
||||
|
||||
Reference in New Issue
Block a user