8a0fa93aec
* Zanzana: Fix duplicated writes * add tests
126 lines
3.7 KiB
Go
126 lines
3.7 KiB
Go
package server
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
|
|
openfgav1 "github.com/openfga/api/proto/openfga/v1"
|
|
|
|
authzextv1 "github.com/grafana/grafana/pkg/services/authz/proto/v1"
|
|
zanzana "github.com/grafana/grafana/pkg/services/authz/zanzana/common"
|
|
)
|
|
|
|
func (s *Server) mutateFolders(ctx context.Context, store *storeInfo, operations []*authzextv1.MutateOperation) error {
|
|
ctx, span := s.tracer.Start(ctx, "server.mutateFolder")
|
|
defer span.End()
|
|
|
|
writeTuples := make([]*openfgav1.TupleKey, 0)
|
|
deleteTuples := make([]*openfgav1.TupleKeyWithoutCondition, 0)
|
|
|
|
for _, operation := range operations {
|
|
switch op := operation.Operation.(type) {
|
|
case *authzextv1.MutateOperation_SetFolderParent:
|
|
tuple, err := s.getFolderWriteTuple(ctx, store, op.SetFolderParent)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if tuple != nil {
|
|
writeTuples = append(writeTuples, tuple)
|
|
}
|
|
|
|
// Delete existing parent tuples
|
|
if op.SetFolderParent.GetDeleteExisting() {
|
|
tuples, err := s.getFolderDeleteTuples(ctx, store, op.SetFolderParent.GetFolder(), op.SetFolderParent.GetParent(), true)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
deleteTuples = append(deleteTuples, tuples...)
|
|
}
|
|
case *authzextv1.MutateOperation_DeleteFolder:
|
|
tuples, err := s.getFolderDeleteTuples(ctx, store, op.DeleteFolder.GetFolder(), op.DeleteFolder.GetParent(), op.DeleteFolder.GetDeleteExisting())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
deleteTuples = append(deleteTuples, tuples...)
|
|
default:
|
|
s.logger.Debug("unsupported mutate operation", "operation", op)
|
|
}
|
|
}
|
|
|
|
if len(writeTuples) == 0 && len(deleteTuples) == 0 {
|
|
return nil
|
|
}
|
|
|
|
err := s.writeTuples(ctx, store, writeTuples, deleteTuples)
|
|
if err != nil {
|
|
s.logger.Error("failed to write folder tuples", "error", err)
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (s *Server) getFolderWriteTuple(ctx context.Context, store *storeInfo, req *authzextv1.SetFolderParentOperation) (*openfgav1.TupleKey, error) {
|
|
// Folder is at the root level
|
|
if req.GetParent() == "" {
|
|
return nil, nil
|
|
}
|
|
|
|
if strings.ContainsAny(req.GetFolder(), "#:") {
|
|
return nil, fmt.Errorf("folder UID contains invalid characters: %s", req.GetFolder())
|
|
}
|
|
|
|
tuple := zanzana.NewFolderParentTuple(req.GetFolder(), req.GetParent())
|
|
return tuple, nil
|
|
}
|
|
|
|
func (s *Server) getFolderDeleteTuples(ctx context.Context, store *storeInfo, folderUID string, parentUID string, deleteExisting bool) ([]*openfgav1.TupleKeyWithoutCondition, error) {
|
|
tupleKeysToDelete := make([]*openfgav1.TupleKeyWithoutCondition, 0)
|
|
|
|
if folderUID != "" && parentUID != "" && !deleteExisting {
|
|
tuple := zanzana.NewFolderParentTuple(folderUID, parentUID)
|
|
tupleKeysToDelete = append(tupleKeysToDelete, &openfgav1.TupleKeyWithoutCondition{
|
|
User: tuple.GetUser(),
|
|
Relation: tuple.GetRelation(),
|
|
Object: tuple.GetObject(),
|
|
})
|
|
}
|
|
|
|
if deleteExisting {
|
|
parentTuples, err := s.listFolderParents(ctx, store, folderUID)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to list folder parents: %w", err)
|
|
}
|
|
|
|
for _, tuple := range parentTuples {
|
|
tupleKeysToDelete = append(tupleKeysToDelete, &openfgav1.TupleKeyWithoutCondition{
|
|
User: tuple.Key.User,
|
|
Relation: tuple.Key.Relation,
|
|
Object: tuple.Key.Object,
|
|
})
|
|
}
|
|
}
|
|
|
|
return tupleKeysToDelete, nil
|
|
}
|
|
|
|
func (s *Server) listFolderParents(ctx context.Context, store *storeInfo, folderUID string) ([]*openfgav1.Tuple, error) {
|
|
ctx, span := s.tracer.Start(ctx, "server.listFolderParents")
|
|
defer span.End()
|
|
|
|
object := zanzana.NewFolderIdent(folderUID)
|
|
resp, err := s.openfga.Read(ctx, &openfgav1.ReadRequest{
|
|
StoreId: store.ID,
|
|
TupleKey: &openfgav1.ReadRequestTupleKey{
|
|
Object: object,
|
|
Relation: zanzana.RelationParent,
|
|
},
|
|
})
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to list folder parents: %w", err)
|
|
}
|
|
|
|
return resp.Tuples, nil
|
|
}
|