From 747bb6925b331e8f505e1de24fb9e70363ce2b29 Mon Sep 17 00:00:00 2001 From: Gert Drapers Date: Tue, 3 Oct 2023 16:01:23 -0700 Subject: [PATCH] v3 isolated --- pkg/directory/v2/importer.go | 98 ++++---- pkg/directory/v2/reader.go | 415 +++++++++++++++---------------- pkg/directory/v2/writer.go | 215 +++++++++-------- pkg/directory/v3/exporter.go | 34 +-- pkg/directory/v3/importer.go | 32 +-- pkg/directory/v3/reader.go | 456 ++++++++++++++--------------------- pkg/directory/v3/writer.go | 223 +++++++++-------- pkg/ds/check_permission.go | 62 +++-- pkg/ds/check_relation.go | 67 ++--- pkg/ds/const.go | 1 + pkg/ds/error.go | 10 +- pkg/ds/graph.go | 166 +++++++------ pkg/ds/object.go | 61 +++-- pkg/ds/relation.go | 403 +++++++++++++++---------------- pkg/ds/userset.go | 12 +- 15 files changed, 1083 insertions(+), 1172 deletions(-) diff --git a/pkg/directory/v2/importer.go b/pkg/directory/v2/importer.go index 288de51..d3bac7b 100644 --- a/pkg/directory/v2/importer.go +++ b/pkg/directory/v2/importer.go @@ -6,9 +6,7 @@ import ( dsc2 "github.com/aserto-dev/go-directory/aserto/directory/common/v2" dsi2 "github.com/aserto-dev/go-directory/aserto/directory/importer/v2" - "github.com/aserto-dev/go-directory/pkg/derr" "github.com/aserto-dev/go-edge-ds/pkg/bdb" - "github.com/aserto-dev/go-edge-ds/pkg/ds" "github.com/aserto-dev/go-edge-ds/pkg/session" "github.com/google/uuid" @@ -96,17 +94,17 @@ func (s *Importer) handleImportRequest(ctx context.Context, tx *bolt.Tx, req *ds func (s *Importer) objectTypeHandler(ctx context.Context, tx *bolt.Tx, req *dsc2.ObjectType) error { s.logger.Debug().Interface("objectType", req).Msg("import_object_type") - if req == nil { - return derr.ErrInvalidObjectType.Msg("nil") - } + // if req == nil { + // return derr.ErrInvalidObjectType.Msg("nil") + // } - if ok, err := ds.ObjectType(req).Validate(); !ok { - return err - } + // if ok, err := ds.ObjectType(req).Validate(); !ok { + // return err + // } - if _, err := bdb.Set(ctx, tx, bdb.ObjectTypesPath, ds.ObjectType(req).Key(), req); err != nil { - return derr.ErrInvalidObjectType.Msg("set") - } + // if _, err := bdb.Set(ctx, tx, bdb.ObjectTypesPath, ds.ObjectType(req).Key(), req); err != nil { + // return derr.ErrInvalidObjectType.Msg("set") + // } return nil } @@ -114,17 +112,17 @@ func (s *Importer) objectTypeHandler(ctx context.Context, tx *bolt.Tx, req *dsc2 func (s *Importer) permissionHandler(ctx context.Context, tx *bolt.Tx, req *dsc2.Permission) error { s.logger.Debug().Interface("permission", req).Msg("import_permission") - if req == nil { - return derr.ErrInvalidPermission.Msg("nil") - } + // if req == nil { + // return derr.ErrInvalidPermission.Msg("nil") + // } - if ok, err := ds.Permission(req).Validate(); !ok { - return err - } + // if ok, err := ds.Permission(req).Validate(); !ok { + // return err + // } - if _, err := bdb.Set(ctx, tx, bdb.PermissionsPath, ds.Permission(req).Key(), req); err != nil { - return derr.ErrInvalidPermission.Msg("set") - } + // if _, err := bdb.Set(ctx, tx, bdb.PermissionsPath, ds.Permission(req).Key(), req); err != nil { + // return derr.ErrInvalidPermission.Msg("set") + // } return nil } @@ -132,17 +130,17 @@ func (s *Importer) permissionHandler(ctx context.Context, tx *bolt.Tx, req *dsc2 func (s *Importer) relationTypeHandler(ctx context.Context, tx *bolt.Tx, req *dsc2.RelationType) error { s.logger.Debug().Interface("relationType", req).Msg("import_relation_type") - if req == nil { - return derr.ErrInvalidRelationType.Msg("nil") - } + // if req == nil { + // return derr.ErrInvalidRelationType.Msg("nil") + // } - if ok, err := ds.RelationType(req).Validate(s.store.MC()); !ok { - return err - } + // if ok, err := ds.RelationType(req).Validate(s.store.MC()); !ok { + // return err + // } - if _, err := bdb.Set(ctx, tx, bdb.RelationTypesPath, ds.RelationType(req).Key(), req); err != nil { - return derr.ErrInvalidRelationType.Msg("set") - } + // if _, err := bdb.Set(ctx, tx, bdb.RelationTypesPath, ds.RelationType(req).Key(), req); err != nil { + // return derr.ErrInvalidRelationType.Msg("set") + // } return nil } @@ -150,17 +148,17 @@ func (s *Importer) relationTypeHandler(ctx context.Context, tx *bolt.Tx, req *ds func (s *Importer) objectHandler(ctx context.Context, tx *bolt.Tx, req *dsc2.Object) error { s.logger.Debug().Interface("object", req).Msg("import_object") - if req == nil { - return derr.ErrInvalidObject.Msg("nil") - } + // if req == nil { + // return derr.ErrInvalidObject.Msg("nil") + // } - if ok, err := ds.Object(req).Validate(s.store.MC()); !ok { - return err - } + // if ok, err := ds.Object(req).Validate(s.store.MC()); !ok { + // return err + // } - if _, err := bdb.Set(ctx, tx, bdb.ObjectsPath, ds.Object(req).Key(), req); err != nil { - return derr.ErrInvalidObject.Msg("set") - } + // if _, err := bdb.Set(ctx, tx, bdb.ObjectsPath, ds.Object(req).Key(), req); err != nil { + // return derr.ErrInvalidObject.Msg("set") + // } return nil } @@ -168,21 +166,21 @@ func (s *Importer) objectHandler(ctx context.Context, tx *bolt.Tx, req *dsc2.Obj func (s *Importer) relationHandler(ctx context.Context, tx *bolt.Tx, req *dsc2.Relation) error { s.logger.Debug().Interface("relation", req).Msg("import_relation") - if req == nil { - return derr.ErrInvalidRelation.Msg("nil") - } + // if req == nil { + // return derr.ErrInvalidRelation.Msg("nil") + // } - if ok, err := ds.Relation(req).Validate(s.store.MC()); !ok { - return err - } + // if ok, err := ds.Relation(req).Validate(s.store.MC()); !ok { + // return err + // } - if _, err := bdb.Set(ctx, tx, bdb.RelationsObjPath, ds.Relation(req).ObjKey(), req); err != nil { - return derr.ErrInvalidRelation.Msg("set") - } + // if _, err := bdb.Set(ctx, tx, bdb.RelationsObjPath, ds.Relation(req).ObjKey(), req); err != nil { + // return derr.ErrInvalidRelation.Msg("set") + // } - if _, err := bdb.Set(ctx, tx, bdb.RelationsSubPath, ds.Relation(req).SubKey(), req); err != nil { - return derr.ErrInvalidRelation.Msg("set") - } + // if _, err := bdb.Set(ctx, tx, bdb.RelationsSubPath, ds.Relation(req).SubKey(), req); err != nil { + // return derr.ErrInvalidRelation.Msg("set") + // } return nil } diff --git a/pkg/directory/v2/reader.go b/pkg/directory/v2/reader.go index e16e9fa..03350e8 100644 --- a/pkg/directory/v2/reader.go +++ b/pkg/directory/v2/reader.go @@ -7,9 +7,10 @@ import ( dsr2 "github.com/aserto-dev/go-directory/aserto/directory/reader/v2" "github.com/aserto-dev/go-edge-ds/pkg/bdb" "github.com/aserto-dev/go-edge-ds/pkg/ds" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" "github.com/rs/zerolog" - bolt "go.etcd.io/bbolt" ) type Reader struct { @@ -153,267 +154,267 @@ func (s *Reader) GetPermissions(ctx context.Context, req *dsr2.GetPermissionsReq func (s *Reader) GetObject(ctx context.Context, req *dsr2.GetObjectRequest) (*dsr2.GetObjectResponse, error) { resp := &dsr2.GetObjectResponse{} - if ok, err := ds.ObjectIdentifier(req.Param).Validate(); !ok { - return resp, err - } - - err := s.store.DB().View(func(tx *bolt.Tx) error { - obj, err := bdb.Get[dsc2.Object](ctx, tx, bdb.ObjectsPath, ds.ObjectIdentifier(req.Param).Key()) - if err != nil { - return err - } - - if req.GetWithRelations() { - // incoming object relations of object instance (result.type == incoming.subject.type && result.key == incoming.subject.key) - incoming, err := bdb.Scan[dsc2.Relation](ctx, tx, bdb.RelationsSubPath, ds.Object(obj).Key()) - if err != nil { - return err - } - resp.Relations = append(resp.Relations, incoming...) - - // outgoing object relations of object instance (result.type == outgoing.object.type && result.key == outgoing.object.key) - outgoing, err := bdb.Scan[dsc2.Relation](ctx, tx, bdb.RelationsObjPath, ds.Object(obj).Key()) - if err != nil { - return err - } - resp.Relations = append(resp.Relations, outgoing...) - - s.logger.Trace().Msg("get object with relations") - } - - resp.Result = obj - return nil - }) - - return resp, err + // if ok, err := ds.ObjectIdentifier(req.Param).Validate(); !ok { + // return resp, err + // } + + // err := s.store.DB().View(func(tx *bolt.Tx) error { + // obj, err := bdb.Get[dsc2.Object](ctx, tx, bdb.ObjectsPath, ds.ObjectIdentifier(req.Param).Key()) + // if err != nil { + // return err + // } + + // if req.GetWithRelations() { + // // incoming object relations of object instance (result.type == incoming.subject.type && result.key == incoming.subject.key) + // incoming, err := bdb.Scan[dsc2.Relation](ctx, tx, bdb.RelationsSubPath, ds.Object(obj).Key()) + // if err != nil { + // return err + // } + // resp.Relations = append(resp.Relations, incoming...) + + // // outgoing object relations of object instance (result.type == outgoing.object.type && result.key == outgoing.object.key) + // outgoing, err := bdb.Scan[dsc2.Relation](ctx, tx, bdb.RelationsObjPath, ds.Object(obj).Key()) + // if err != nil { + // return err + // } + // resp.Relations = append(resp.Relations, outgoing...) + + // s.logger.Trace().Msg("get object with relations") + // } + + // resp.Result = obj + // return nil + // }) + + return resp, status.Error(codes.Unimplemented, "GetObject") } // Get multiple object instances by id or type+key, in a single request. func (s *Reader) GetObjectMany(ctx context.Context, req *dsr2.GetObjectManyRequest) (*dsr2.GetObjectManyResponse, error) { resp := &dsr2.GetObjectManyResponse{Results: []*dsc2.Object{}} - if req.Param == nil { - req.Param = []*dsc2.ObjectIdentifier{} - } - - // validate all object identifiers first. - for _, i := range req.Param { - if ok, err := ds.ObjectIdentifier(i).Validate(); !ok { - return resp, err - } - } - - err := s.store.DB().View(func(tx *bolt.Tx) error { - for _, i := range req.Param { - obj, err := bdb.Get[dsc2.Object](ctx, tx, bdb.ObjectsPath, ds.ObjectIdentifier(i).Key()) - if err != nil { - return err - } - resp.Results = append(resp.Results, obj) - } - return nil - }) - - return resp, err + // if req.Param == nil { + // req.Param = []*dsc2.ObjectIdentifier{} + // } + + // // validate all object identifiers first. + // for _, i := range req.Param { + // if ok, err := ds.ObjectIdentifier(i).Validate(); !ok { + // return resp, err + // } + // } + + // err := s.store.DB().View(func(tx *bolt.Tx) error { + // for _, i := range req.Param { + // obj, err := bdb.Get[dsc2.Object](ctx, tx, bdb.ObjectsPath, ds.ObjectIdentifier(i).Key()) + // if err != nil { + // return err + // } + // resp.Results = append(resp.Results, obj) + // } + // return nil + // }) + + return resp, status.Error(codes.Unimplemented, "GetObjectMany") } // Get all object instances, optionally filtered by object type. (paginated). func (s *Reader) GetObjects(ctx context.Context, req *dsr2.GetObjectsRequest) (*dsr2.GetObjectsResponse, error) { resp := &dsr2.GetObjectsResponse{Results: []*dsc2.Object{}, Page: &dsc2.PaginationResponse{}} - if req.Param == nil { - req.Param = &dsc2.ObjectTypeIdentifier{} - } + // if req.Param == nil { + // req.Param = &dsc2.ObjectTypeIdentifier{} + // } - if req.Page == nil { - req.Page = &dsc2.PaginationRequest{Size: 100} - } + // if req.Page == nil { + // req.Page = &dsc2.PaginationRequest{Size: 100} + // } - if ok, err := ds.ObjectTypeSelector(req.Param).Validate(); !ok { - return resp, err - } + // if ok, err := ds.ObjectTypeSelector(req.Param).Validate(); !ok { + // return resp, err + // } - opts := []bdb.ScanOption{ - bdb.WithPageSize(req.Page.Size), - bdb.WithPageToken(req.Page.Token), - bdb.WithKeyFilter(req.Param.GetName()), - } + // opts := []bdb.ScanOption{ + // bdb.WithPageSize(req.Page.Size), + // bdb.WithPageToken(req.Page.Token), + // bdb.WithKeyFilter(req.Param.GetName()), + // } - err := s.store.DB().View(func(tx *bolt.Tx) error { - iter, err := bdb.NewPageIterator[dsc2.Object](ctx, tx, bdb.ObjectsPath, opts...) - if err != nil { - return err - } + // err := s.store.DB().View(func(tx *bolt.Tx) error { + // iter, err := bdb.NewPageIterator[dsc2.Object](ctx, tx, bdb.ObjectsPath, opts...) + // if err != nil { + // return err + // } - iter.Next() + // iter.Next() - resp.Results = iter.Value() - resp.Page = &dsc2.PaginationResponse{ - NextToken: iter.NextToken(), - ResultSize: int32(len(resp.Results)), - } + // resp.Results = iter.Value() + // resp.Page = &dsc2.PaginationResponse{ + // NextToken: iter.NextToken(), + // ResultSize: int32(len(resp.Results)), + // } - return nil - }) + // return nil + // }) - return resp, err + return resp, status.Error(codes.Unimplemented, "GetObjects") } // Get relation instances based on subject, relation, object filter. func (s *Reader) GetRelation(ctx context.Context, req *dsr2.GetRelationRequest) (*dsr2.GetRelationResponse, error) { resp := &dsr2.GetRelationResponse{Results: []*dsc2.Relation{}, Objects: map[string]*dsc2.Object{}} - if ok, err := ds.RelationIdentifier(req.Param).Validate(); !ok { - return resp, err - } - - err := s.store.DB().View(func(tx *bolt.Tx) error { - relations, err := bdb.Scan[dsc2.Relation](ctx, tx, bdb.RelationsObjPath, ds.RelationIdentifier(req.Param).ObjKey()) - if err != nil { - return err - } - - if len(relations) == 0 { - return bdb.ErrKeyNotFound - } - if len(relations) != 1 { - return bdb.ErrMultipleResults - } - - rel := relations[0] - resp.Results = append(resp.Results, rel) - - if req.GetWithObjects() { - objects := map[string]*dsc2.Object{} - for i := 0; i < len(resp.Results); i++ { - sub, err := bdb.Get[dsc2.Object](ctx, tx, bdb.ObjectsPath, ds.ObjectIdentifier(rel.Subject).Key()) - if err != nil { - return err - } - objects[ds.ObjectIdentifier(rel.Subject).Key()] = sub - - obj, err := bdb.Get[dsc2.Object](ctx, tx, bdb.ObjectsPath, ds.ObjectIdentifier(rel.Object).Key()) - if err != nil { - return err - } - objects[ds.ObjectIdentifier(rel.Object).Key()] = obj - } - resp.Objects = objects - } - - return nil - }) - - return resp, err + // if ok, err := ds.RelationIdentifier(req.Param).Validate(); !ok { + // return resp, err + // } + + // err := s.store.DB().View(func(tx *bolt.Tx) error { + // relations, err := bdb.Scan[dsc2.Relation](ctx, tx, bdb.RelationsObjPath, ds.RelationIdentifier(req.Param).ObjKey()) + // if err != nil { + // return err + // } + + // if len(relations) == 0 { + // return bdb.ErrKeyNotFound + // } + // if len(relations) != 1 { + // return bdb.ErrMultipleResults + // } + + // rel := relations[0] + // resp.Results = append(resp.Results, rel) + + // if req.GetWithObjects() { + // objects := map[string]*dsc2.Object{} + // for i := 0; i < len(resp.Results); i++ { + // sub, err := bdb.Get[dsc2.Object](ctx, tx, bdb.ObjectsPath, ds.ObjectIdentifier(rel.Subject).Key()) + // if err != nil { + // return err + // } + // objects[ds.ObjectIdentifier(rel.Subject).Key()] = sub + + // obj, err := bdb.Get[dsc2.Object](ctx, tx, bdb.ObjectsPath, ds.ObjectIdentifier(rel.Object).Key()) + // if err != nil { + // return err + // } + // objects[ds.ObjectIdentifier(rel.Object).Key()] = obj + // } + // resp.Objects = objects + // } + + // return nil + // }) + + return resp, status.Error(codes.Unimplemented, "GetRelation") } // Get relation instances based on subject, relation, object filter (paginated). func (s *Reader) GetRelations(ctx context.Context, req *dsr2.GetRelationsRequest) (*dsr2.GetRelationsResponse, error) { resp := &dsr2.GetRelationsResponse{Results: []*dsc2.Relation{}, Page: &dsc2.PaginationResponse{}} - if req.Page == nil { - req.Page = &dsc2.PaginationRequest{Size: 100} - } - - if req.Param == nil { - req.Param = &dsc2.RelationIdentifier{ - Object: &dsc2.ObjectIdentifier{}, - Relation: &dsc2.RelationTypeIdentifier{}, - Subject: &dsc2.ObjectIdentifier{}, - } - } - - if ok, err := ds.RelationSelector(req.Param).Validate(); !ok { - return resp, err - } - - path, keyFilter, valueFilter := ds.RelationSelector(req.Param).Filter() - - opts := []bdb.ScanOption{ - bdb.WithPageToken(req.Page.Token), - bdb.WithKeyFilter(keyFilter), - } - - err := s.store.DB().View(func(tx *bolt.Tx) error { - iter, err := bdb.NewScanIterator[dsc2.Relation](ctx, tx, path, opts...) - if err != nil { - return err - } - - for iter.Next() { - if !valueFilter(iter.Value()) { - continue - } - resp.Results = append(resp.Results, iter.Value()) - - if req.Page.Size == int32(len(resp.Results)) { - if iter.Next() { - resp.Page.NextToken = iter.Key() - } - break - } - } - - resp.Page.ResultSize = int32(len(resp.Results)) - - return nil - }) - - return resp, err + // if req.Page == nil { + // req.Page = &dsc2.PaginationRequest{Size: 100} + // } + + // if req.Param == nil { + // req.Param = &dsc2.RelationIdentifier{ + // Object: &dsc2.ObjectIdentifier{}, + // Relation: &dsc2.RelationTypeIdentifier{}, + // Subject: &dsc2.ObjectIdentifier{}, + // } + // } + + // if ok, err := ds.RelationSelector(req.Param).Validate(); !ok { + // return resp, err + // } + + // path, keyFilter, valueFilter := ds.RelationSelector(req.Param).Filter() + + // opts := []bdb.ScanOption{ + // bdb.WithPageToken(req.Page.Token), + // bdb.WithKeyFilter(keyFilter), + // } + + // err := s.store.DB().View(func(tx *bolt.Tx) error { + // iter, err := bdb.NewScanIterator[dsc2.Relation](ctx, tx, path, opts...) + // if err != nil { + // return err + // } + + // for iter.Next() { + // if !valueFilter(iter.Value()) { + // continue + // } + // resp.Results = append(resp.Results, iter.Value()) + + // if req.Page.Size == int32(len(resp.Results)) { + // if iter.Next() { + // resp.Page.NextToken = iter.Key() + // } + // break + // } + // } + + // resp.Page.ResultSize = int32(len(resp.Results)) + + // return nil + // }) + + return resp, status.Error(codes.Unimplemented, "GetRelations") } // Check if subject has permission on object. func (s *Reader) CheckPermission(ctx context.Context, req *dsr2.CheckPermissionRequest) (*dsr2.CheckPermissionResponse, error) { resp := &dsr2.CheckPermissionResponse{} - if ok, err := ds.CheckPermission(req).Validate(); !ok { - return resp, err - } + // if ok, err := ds.CheckPermission(req).Validate(); !ok { + // return resp, err + // } - err := s.store.DB().View(func(tx *bolt.Tx) error { - var err error - resp, err = ds.CheckPermission(req).Exec(ctx, tx, s.store.MC()) - return err - }) + // err := s.store.DB().View(func(tx *bolt.Tx) error { + // var err error + // resp, err = ds.CheckPermission(req).Exec(ctx, tx, s.store.MC()) + // return err + // }) - return resp, err + return resp, status.Error(codes.Unimplemented, "CheckPermission") } // Check if subject has relation to object. func (s *Reader) CheckRelation(ctx context.Context, req *dsr2.CheckRelationRequest) (*dsr2.CheckRelationResponse, error) { resp := &dsr2.CheckRelationResponse{} - if ok, err := ds.CheckRelation(req).Validate(); !ok { - return resp, err - } + // if ok, err := ds.CheckRelation(req).Validate(); !ok { + // return resp, err + // } - err := s.store.DB().View(func(tx *bolt.Tx) error { - var err error - resp, err = ds.CheckRelation(req).Exec(ctx, tx, s.store.MC()) - return err - }) + // err := s.store.DB().View(func(tx *bolt.Tx) error { + // var err error + // resp, err = ds.CheckRelation(req).Exec(ctx, tx, s.store.MC()) + // return err + // }) - return resp, err + return resp, status.Error(codes.Unimplemented, "CheckRelation") } // Get object dependency graph. func (s *Reader) GetGraph(ctx context.Context, req *dsr2.GetGraphRequest) (*dsr2.GetGraphResponse, error) { resp := &dsr2.GetGraphResponse{} - if ok, err := ds.GetGraph(req).Validate(); !ok { - return resp, err - } + // if ok, err := ds.GetGraph(req).Validate(); !ok { + // return resp, err + // } - err := s.store.DB().View(func(tx *bolt.Tx) error { - var err error - results, err := ds.GetGraph(req).Exec(ctx, tx) - if err != nil { - return err - } + // err := s.store.DB().View(func(tx *bolt.Tx) error { + // var err error + // results, err := ds.GetGraph(req).Exec(ctx, tx) + // if err != nil { + // return err + // } - resp.Results = results - return nil - }) + // resp.Results = results + // return nil + // }) - return resp, err + return resp, status.Error(codes.Unimplemented, "GetGraph") } diff --git a/pkg/directory/v2/writer.go b/pkg/directory/v2/writer.go index d6f759b..2ea2aba 100644 --- a/pkg/directory/v2/writer.go +++ b/pkg/directory/v2/writer.go @@ -3,13 +3,14 @@ package v2 import ( "context" - dsc2 "github.com/aserto-dev/go-directory/aserto/directory/common/v2" dsw2 "github.com/aserto-dev/go-directory/aserto/directory/writer/v2" "github.com/aserto-dev/go-edge-ds/pkg/bdb" "github.com/aserto-dev/go-edge-ds/pkg/ds" "github.com/rs/zerolog" bolt "go.etcd.io/bbolt" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" "google.golang.org/protobuf/types/known/emptypb" ) @@ -191,138 +192,138 @@ func (s *Writer) DeletePermission(ctx context.Context, req *dsw2.DeletePermissio func (s *Writer) SetObject(ctx context.Context, req *dsw2.SetObjectRequest) (*dsw2.SetObjectResponse, error) { resp := &dsw2.SetObjectResponse{} - if ok, err := ds.Object(req.Object).Validate(s.store.MC()); !ok { - return resp, err - } + // if ok, err := ds.Object(req.Object).Validate(s.store.MC()); !ok { + // return resp, err + // } - req.Object.Hash = ds.Object(req.Object).Hash() + // req.Object.Hash = ds.Object(req.Object).Hash() - err := s.store.DB().Update(func(tx *bolt.Tx) error { - updReq, err := bdb.UpdateMetadata(ctx, tx, bdb.ObjectsPath, ds.Object(req.Object).Key(), req.Object) - if err != nil { - return err - } + // err := s.store.DB().Update(func(tx *bolt.Tx) error { + // updReq, err := bdb.UpdateMetadata(ctx, tx, bdb.ObjectsPath, ds.Object(req.Object).Key(), req.Object) + // if err != nil { + // return err + // } - objType, err := bdb.Set(ctx, tx, bdb.ObjectsPath, ds.Object(req.Object).Key(), updReq) - if err != nil { - return err - } + // objType, err := bdb.Set(ctx, tx, bdb.ObjectsPath, ds.Object(req.Object).Key(), updReq) + // if err != nil { + // return err + // } - resp.Result = objType - return nil - }) + // resp.Result = objType + // return nil + // }) - return resp, err + return resp, status.Error(codes.Unimplemented, "SetObject") } func (s *Writer) DeleteObject(ctx context.Context, req *dsw2.DeleteObjectRequest) (*dsw2.DeleteObjectResponse, error) { resp := &dsw2.DeleteObjectResponse{} - if ok, err := ds.ObjectIdentifier(req.Param).Validate(); !ok { - return resp, err - } - - err := s.store.DB().Update(func(tx *bolt.Tx) error { - if err := bdb.Delete(ctx, tx, bdb.ObjectsPath, ds.ObjectIdentifier(req.Param).Key()); err != nil { - return err - } - - if req.GetWithRelations() { - { - // incoming object relations of object instance (result.type == incoming.subject.type && result.key == incoming.subject.key) - iter, err := bdb.NewScanIterator[dsc2.Relation](ctx, tx, bdb.RelationsSubPath, bdb.WithKeyFilter(ds.ObjectIdentifier(req.Param).Key()+ds.InstanceSeparator)) - if err != nil { - return err - } - - for iter.Next() { - if err := bdb.Delete(ctx, tx, bdb.RelationsObjPath, ds.Relation(iter.Value()).ObjKey()); err != nil { - return err - } - - if err := bdb.Delete(ctx, tx, bdb.RelationsSubPath, ds.Relation(iter.Value()).SubKey()); err != nil { - return err - } - } - } - { - // outgoing object relations of object instance (result.type == outgoing.object.type && result.key == outgoing.object.key) - iter, err := bdb.NewScanIterator[dsc2.Relation](ctx, tx, bdb.RelationsObjPath, bdb.WithKeyFilter(ds.ObjectIdentifier(req.Param).Key()+ds.InstanceSeparator)) - if err != nil { - return err - } - - for iter.Next() { - if err := bdb.Delete(ctx, tx, bdb.RelationsObjPath, ds.Relation(iter.Value()).ObjKey()); err != nil { - return err - } - - if err := bdb.Delete(ctx, tx, bdb.RelationsSubPath, ds.Relation(iter.Value()).SubKey()); err != nil { - return err - } - } - } - } - - resp.Result = &emptypb.Empty{} - return nil - }) - - return resp, err + // if ok, err := ds.ObjectIdentifier(req.Param).Validate(); !ok { + // return resp, err + // } + + // err := s.store.DB().Update(func(tx *bolt.Tx) error { + // if err := bdb.Delete(ctx, tx, bdb.ObjectsPath, ds.ObjectIdentifier(req.Param).Key()); err != nil { + // return err + // } + + // if req.GetWithRelations() { + // { + // // incoming object relations of object instance (result.type == incoming.subject.type && result.key == incoming.subject.key) + // iter, err := bdb.NewScanIterator[dsc2.Relation](ctx, tx, bdb.RelationsSubPath, bdb.WithKeyFilter(ds.ObjectIdentifier(req.Param).Key()+ds.InstanceSeparator)) + // if err != nil { + // return err + // } + + // for iter.Next() { + // if err := bdb.Delete(ctx, tx, bdb.RelationsObjPath, ds.Relation(iter.Value()).ObjKey()); err != nil { + // return err + // } + + // if err := bdb.Delete(ctx, tx, bdb.RelationsSubPath, ds.Relation(iter.Value()).SubKey()); err != nil { + // return err + // } + // } + // } + // { + // // outgoing object relations of object instance (result.type == outgoing.object.type && result.key == outgoing.object.key) + // iter, err := bdb.NewScanIterator[dsc2.Relation](ctx, tx, bdb.RelationsObjPath, bdb.WithKeyFilter(ds.ObjectIdentifier(req.Param).Key()+ds.InstanceSeparator)) + // if err != nil { + // return err + // } + + // for iter.Next() { + // if err := bdb.Delete(ctx, tx, bdb.RelationsObjPath, ds.Relation(iter.Value()).ObjKey()); err != nil { + // return err + // } + + // if err := bdb.Delete(ctx, tx, bdb.RelationsSubPath, ds.Relation(iter.Value()).SubKey()); err != nil { + // return err + // } + // } + // } + // } + + // resp.Result = &emptypb.Empty{} + // return nil + // }) + + return resp, status.Error(codes.Unimplemented, "DeleteObject") } // relation methods. func (s *Writer) SetRelation(ctx context.Context, req *dsw2.SetRelationRequest) (*dsw2.SetRelationResponse, error) { resp := &dsw2.SetRelationResponse{} - if ok, err := ds.Relation(req.Relation).Validate(s.store.MC()); !ok { - return resp, err - } + // if ok, err := ds.Relation(req.Relation).Validate(s.store.MC()); !ok { + // return resp, err + // } - req.Relation.Hash = ds.Relation(req.Relation).Hash() + // req.Relation.Hash = ds.Relation(req.Relation).Hash() - err := s.store.DB().Update(func(tx *bolt.Tx) error { - updReq, err := bdb.UpdateMetadata(ctx, tx, bdb.RelationsObjPath, ds.Relation(req.Relation).ObjKey(), req.Relation) - if err != nil { - return err - } + // err := s.store.DB().Update(func(tx *bolt.Tx) error { + // updReq, err := bdb.UpdateMetadata(ctx, tx, bdb.RelationsObjPath, ds.Relation(req.Relation).ObjKey(), req.Relation) + // if err != nil { + // return err + // } - objRel, err := bdb.Set(ctx, tx, bdb.RelationsObjPath, ds.Relation(req.Relation).ObjKey(), updReq) - if err != nil { - return err - } + // objRel, err := bdb.Set(ctx, tx, bdb.RelationsObjPath, ds.Relation(req.Relation).ObjKey(), updReq) + // if err != nil { + // return err + // } - subRel, err := bdb.Set(ctx, tx, bdb.RelationsSubPath, ds.Relation(req.Relation).SubKey(), req.Relation) - if err != nil { - return err - } + // subRel, err := bdb.Set(ctx, tx, bdb.RelationsSubPath, ds.Relation(req.Relation).SubKey(), req.Relation) + // if err != nil { + // return err + // } - resp.Result = objRel - _ = subRel + // resp.Result = objRel + // _ = subRel - return nil - }) + // return nil + // }) - return resp, err + return resp, status.Error(codes.Unimplemented, "SetRelation") } func (s *Writer) DeleteRelation(ctx context.Context, req *dsw2.DeleteRelationRequest) (*dsw2.DeleteRelationResponse, error) { resp := &dsw2.DeleteRelationResponse{} - if ok, err := ds.RelationIdentifier(req.Param).Validate(); !ok { - return resp, err - } - - err := s.store.DB().Update(func(tx *bolt.Tx) error { - if err := bdb.Delete(ctx, tx, bdb.RelationsObjPath, ds.RelationIdentifier(req.Param).ObjKey()); err != nil { - return err - } - if err := bdb.Delete(ctx, tx, bdb.RelationsSubPath, ds.RelationIdentifier(req.Param).SubKey()); err != nil { - return err - } - resp.Result = &emptypb.Empty{} - return nil - }) - - return resp, err + // if ok, err := ds.RelationIdentifier(req.Param).Validate(); !ok { + // return resp, err + // } + + // err := s.store.DB().Update(func(tx *bolt.Tx) error { + // if err := bdb.Delete(ctx, tx, bdb.RelationsObjPath, ds.RelationIdentifier(req.Param).ObjKey()); err != nil { + // return err + // } + // if err := bdb.Delete(ctx, tx, bdb.RelationsSubPath, ds.RelationIdentifier(req.Param).SubKey()); err != nil { + // return err + // } + // resp.Result = &emptypb.Empty{} + // return nil + // }) + + return resp, status.Error(codes.Unimplemented, "DeleteRelation") } diff --git a/pkg/directory/v3/exporter.go b/pkg/directory/v3/exporter.go index 0b48a95..99d9771 100644 --- a/pkg/directory/v3/exporter.go +++ b/pkg/directory/v3/exporter.go @@ -1,7 +1,6 @@ package v3 import ( - dsc2 "github.com/aserto-dev/go-directory/aserto/directory/common/v2" dsc3 "github.com/aserto-dev/go-directory/aserto/directory/common/v3" dse3 "github.com/aserto-dev/go-directory/aserto/directory/exporter/v3" "github.com/aserto-dev/go-edge-ds/pkg/bdb" @@ -48,25 +47,13 @@ func (s *Exporter) Export(req *dse3.ExportRequest, stream dse3.Exporter_ExportSe } func exportObjects(tx *bolt.Tx, stream dse3.Exporter_ExportServer) error { - iter, err := bdb.NewScanIterator[dsc2.Object](stream.Context(), tx, bdb.ObjectsPath) + iter, err := bdb.NewScanIterator[dsc3.Object](stream.Context(), tx, bdb.ObjectsPath) if err != nil { return err } for iter.Next() { - if err := stream.Send(&dse3.ExportResponse{ - Msg: &dse3.ExportResponse_Object{ - Object: &dsc3.Object{ - Type: iter.Value().GetType(), - Id: iter.Value().GetKey(), - DisplayName: iter.Value().GetDisplayName(), - Properties: iter.Value().GetProperties(), - CreatedAt: iter.Value().GetCreatedAt(), - UpdatedAt: iter.Value().GetUpdatedAt(), - Etag: iter.Value().GetHash(), - }, - }, - }); err != nil { + if err := stream.Send(&dse3.ExportResponse{Msg: &dse3.ExportResponse_Object{Object: iter.Value()}}); err != nil { return err } } @@ -75,26 +62,13 @@ func exportObjects(tx *bolt.Tx, stream dse3.Exporter_ExportServer) error { } func exportRelations(tx *bolt.Tx, stream dse3.Exporter_ExportServer) error { - iter, err := bdb.NewScanIterator[dsc2.Relation](stream.Context(), tx, bdb.RelationsObjPath) + iter, err := bdb.NewScanIterator[dsc3.Relation](stream.Context(), tx, bdb.RelationsObjPath) if err != nil { return err } for iter.Next() { - if err := stream.Send(&dse3.ExportResponse{ - Msg: &dse3.ExportResponse_Relation{ - Relation: &dsc3.Relation{ - ObjectType: iter.Value().GetObject().GetType(), - ObjectId: iter.Value().GetObject().GetKey(), - Relation: iter.Value().GetRelation(), - SubjectType: iter.Value().GetSubject().GetType(), - SubjectId: iter.Value().GetSubject().GetKey(), - CreatedAt: iter.Value().GetCreatedAt(), - UpdatedAt: iter.Value().GetUpdatedAt(), - Etag: iter.Value().GetHash(), - }, - }, - }); err != nil { + if err := stream.Send(&dse3.ExportResponse{Msg: &dse3.ExportResponse_Relation{Relation: iter.Value()}}); err != nil { return err } } diff --git a/pkg/directory/v3/importer.go b/pkg/directory/v3/importer.go index 571092a..6ea91d6 100644 --- a/pkg/directory/v3/importer.go +++ b/pkg/directory/v3/importer.go @@ -4,7 +4,6 @@ import ( "context" "io" - dsc2 "github.com/aserto-dev/go-directory/aserto/directory/common/v2" dsc3 "github.com/aserto-dev/go-directory/aserto/directory/common/v3" dsi3 "github.com/aserto-dev/go-directory/aserto/directory/importer/v3" "github.com/aserto-dev/go-directory/pkg/derr" @@ -77,17 +76,7 @@ func (s *Importer) objectHandler(ctx context.Context, tx *bolt.Tx, req *dsc3.Obj return derr.ErrInvalidObject.Msg("nil") } - o2 := &dsc2.Object{ - Type: req.Type, - Key: req.Id, - DisplayName: req.DisplayName, - Properties: req.Properties, - CreatedAt: req.CreatedAt, - UpdatedAt: req.UpdatedAt, - Hash: req.Etag, - } - - if _, err := bdb.Set(ctx, tx, bdb.ObjectsPath, ds.Object(o2).Key(), o2); err != nil { + if _, err := bdb.Set(ctx, tx, bdb.ObjectsPath, ds.Object(req).Key(), req); err != nil { return derr.ErrInvalidObject.Msg("set") } @@ -101,26 +90,11 @@ func (s *Importer) relationHandler(ctx context.Context, tx *bolt.Tx, req *dsc3.R return derr.ErrInvalidRelation.Msg("nil") } - r2 := &dsc2.Relation{ - Object: &dsc2.ObjectIdentifier{ - Type: &req.ObjectType, - Key: &req.ObjectId, - }, - Relation: req.Relation, - Subject: &dsc2.ObjectIdentifier{ - Type: &req.SubjectType, - Key: &req.SubjectId, - }, - CreatedAt: req.CreatedAt, - UpdatedAt: req.UpdatedAt, - Hash: req.Etag, - } - - if _, err := bdb.Set(ctx, tx, bdb.RelationsObjPath, ds.Relation(r2).ObjKey(), r2); err != nil { + if _, err := bdb.Set(ctx, tx, bdb.RelationsObjPath, ds.Relation(req).ObjKey(), req); err != nil { return derr.ErrInvalidRelation.Msg("set") } - if _, err := bdb.Set(ctx, tx, bdb.RelationsSubPath, ds.Relation(r2).SubKey(), r2); err != nil { + if _, err := bdb.Set(ctx, tx, bdb.RelationsSubPath, ds.Relation(req).SubKey(), req); err != nil { return derr.ErrInvalidRelation.Msg("set") } diff --git a/pkg/directory/v3/reader.go b/pkg/directory/v3/reader.go index f488991..0ddd716 100644 --- a/pkg/directory/v3/reader.go +++ b/pkg/directory/v3/reader.go @@ -3,26 +3,22 @@ package v3 import ( "context" - dsc2 "github.com/aserto-dev/go-directory/aserto/directory/common/v2" dsc3 "github.com/aserto-dev/go-directory/aserto/directory/common/v3" - dsr2 "github.com/aserto-dev/go-directory/aserto/directory/reader/v2" dsr3 "github.com/aserto-dev/go-directory/aserto/directory/reader/v3" "github.com/aserto-dev/go-edge-ds/pkg/bdb" v2 "github.com/aserto-dev/go-edge-ds/pkg/directory/v2" "github.com/aserto-dev/go-edge-ds/pkg/ds" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" - - "google.golang.org/protobuf/proto" "github.com/bufbuild/protovalidate-go" "github.com/rs/zerolog" + bolt "go.etcd.io/bbolt" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" ) type Reader struct { logger *zerolog.Logger store *bdb.BoltDB - r2 dsr2.ReaderServer v *protovalidate.Validator } @@ -31,353 +27,271 @@ func NewReader(logger *zerolog.Logger, store *bdb.BoltDB, r *v2.Reader) *Reader return &Reader{ logger: logger, store: store, - r2: r, v: v, } } -// object methods. +// GetObject, get single object instance. func (s *Reader) GetObject(ctx context.Context, req *dsr3.GetObjectRequest) (*dsr3.GetObjectResponse, error) { + resp := &dsr3.GetObjectResponse{} + if err := s.v.Validate(req); err != nil { - return &dsr3.GetObjectResponse{}, err + return resp, err } - resp, err := s.r2.GetObject(ctx, &dsr2.GetObjectRequest{ - Param: &dsc2.ObjectIdentifier{ - Type: &req.ObjectType, - Key: &req.ObjectId, - }, - WithRelations: &req.WithRelations, - }) - - if err != nil { - return &dsr3.GetObjectResponse{}, err - } + err := s.store.DB().View(func(tx *bolt.Tx) error { + objIdent := ds.ObjectIdentifier(&dsc3.ObjectIdentifier{ObjectType: req.ObjectType, ObjectId: req.ObjectId}) + obj, err := bdb.Get[dsc3.Object](ctx, tx, bdb.ObjectsPath, objIdent.Key()) + if err != nil { + return err + } - relations := make([]*dsc3.Relation, len(resp.Relations)) - for i, r := range resp.Relations { - relations[i] = &dsc3.Relation{ - ObjectType: r.Object.GetType(), - ObjectId: r.Object.GetKey(), - Relation: r.Relation, - SubjectType: r.Subject.GetType(), - SubjectId: r.Subject.GetKey(), - SubjectRelation: "", - CreatedAt: r.CreatedAt, - UpdatedAt: r.UpdatedAt, - Etag: r.Hash, + if req.GetWithRelations() { + // incoming object relations of object instance (result.type == incoming.subject.type && result.key == incoming.subject.key) + incoming, err := bdb.Scan[dsc3.Relation](ctx, tx, bdb.RelationsSubPath, ds.Object(obj).Key()) + if err != nil { + return err + } + resp.Relations = append(resp.Relations, incoming...) + + // outgoing object relations of object instance (result.type == outgoing.object.type && result.key == outgoing.object.key) + outgoing, err := bdb.Scan[dsc3.Relation](ctx, tx, bdb.RelationsObjPath, ds.Object(obj).Key()) + if err != nil { + return err + } + resp.Relations = append(resp.Relations, outgoing...) + + s.logger.Trace().Msg("get object with relations") } - } - return &dsr3.GetObjectResponse{ - Result: &dsc3.Object{ - Type: resp.Result.Type, - Id: resp.Result.Key, - DisplayName: resp.Result.DisplayName, - Properties: resp.Result.Properties, - CreatedAt: resp.Result.CreatedAt, - UpdatedAt: resp.Result.UpdatedAt, - Etag: resp.Result.Hash, - }, - Relations: relations, - }, nil + resp.Result = obj + return nil + }) + + return resp, err } +// GetObjectMany, get multiple object instances by type+id, in a single request. func (s *Reader) GetObjectMany(ctx context.Context, req *dsr3.GetObjectManyRequest) (*dsr3.GetObjectManyResponse, error) { + resp := &dsr3.GetObjectManyResponse{Results: []*dsc3.Object{}} + if err := s.v.Validate(req); err != nil { - return &dsr3.GetObjectManyResponse{}, err + return resp, err } - param := make([]*dsc2.ObjectIdentifier, len(req.Param)) - for i, p := range req.Param { - param[i] = &dsc2.ObjectIdentifier{ - Type: proto.String(p.ObjectType), - Key: proto.String(p.ObjectId), + // validate all object identifiers first. + for _, i := range req.Param { + if ok, err := ds.ObjectIdentifier(i).Validate(); !ok { + return resp, err } } - resp, err := s.r2.GetObjectMany(ctx, &dsr2.GetObjectManyRequest{ - Param: param, - }) - - if err != nil { - return &dsr3.GetObjectManyResponse{}, err - } - - results := make([]*dsc3.Object, len(resp.Results)) - for i, r := range resp.Results { - results[i] = &dsc3.Object{ - Type: r.Type, - Id: r.Key, - DisplayName: r.DisplayName, - Properties: r.Properties, - CreatedAt: r.CreatedAt, - UpdatedAt: r.UpdatedAt, - Etag: r.Hash, + err := s.store.DB().View(func(tx *bolt.Tx) error { + for _, i := range req.Param { + obj, err := bdb.Get[dsc3.Object](ctx, tx, bdb.ObjectsPath, ds.ObjectIdentifier(i).Key()) + if err != nil { + return err + } + resp.Results = append(resp.Results, obj) } - } + return nil + }) - return &dsr3.GetObjectManyResponse{ - Results: results, - }, nil + return resp, err } +// GetObjects, gets (all) object instances, optionally filtered by object type, as a paginated array of objects. func (s *Reader) GetObjects(ctx context.Context, req *dsr3.GetObjectsRequest) (*dsr3.GetObjectsResponse, error) { + resp := &dsr3.GetObjectsResponse{Results: []*dsc3.Object{}, Page: &dsc3.PaginationResponse{}} + if err := s.v.Validate(req); err != nil { - return &dsr3.GetObjectsResponse{}, err + return resp, err } - resp, err := s.r2.GetObjects(ctx, &dsr2.GetObjectsRequest{ - Param: &dsc2.ObjectTypeIdentifier{ - Name: &req.ObjectType, - }, - Page: ds.PaginationRequest2(req.Page), - }) + if req.Page == nil { + req.Page = &dsc3.PaginationRequest{Size: 100} + } - if err != nil { - return &dsr3.GetObjectsResponse{}, err + opts := []bdb.ScanOption{ + bdb.WithPageSize(req.Page.Size), + bdb.WithPageToken(req.Page.Token), + bdb.WithKeyFilter(req.GetObjectType()), } - results := make([]*dsc3.Object, len(resp.Results)) - for i, r := range resp.Results { - results[i] = &dsc3.Object{ - Type: r.Type, - Id: r.Key, - DisplayName: r.DisplayName, - Properties: r.Properties, - CreatedAt: r.CreatedAt, - UpdatedAt: r.UpdatedAt, - Etag: r.Hash, + err := s.store.DB().View(func(tx *bolt.Tx) error { + iter, err := bdb.NewPageIterator[dsc3.Object](ctx, tx, bdb.ObjectsPath, opts...) + if err != nil { + return err } - } - return &dsr3.GetObjectsResponse{ - Results: results, - Page: ds.PaginationResponse3(resp.Page), - }, nil + iter.Next() + + resp.Results = iter.Value() + resp.Page = &dsc3.PaginationResponse{NextToken: iter.NextToken()} + + return nil + }) + + return resp, err } -// relation methods. +// GetRelation, get a single relation instance based on subject, relation, object filter. func (s *Reader) GetRelation(ctx context.Context, req *dsr3.GetRelationRequest) (*dsr3.GetRelationResponse, error) { + resp := &dsr3.GetRelationResponse{Result: &dsc3.Relation{}, Objects: map[string]*dsc3.Object{}} + if err := s.v.Validate(req); err != nil { - return &dsr3.GetRelationResponse{}, err + return resp, err } - resp, err := s.r2.GetRelation(ctx, &dsr2.GetRelationRequest{ - Param: &dsc2.RelationIdentifier{ - Object: &dsc2.ObjectIdentifier{ - Type: &req.ObjectType, - Key: &req.ObjectId, - }, - Relation: &dsc2.RelationTypeIdentifier{ - ObjectType: &req.ObjectType, - Name: &req.Relation, - }, - Subject: &dsc2.ObjectIdentifier{ - Type: &req.SubjectType, - Key: &req.SubjectId, - }, - }, - WithObjects: &req.WithObjects, - }) - - if err != nil { - return &dsr3.GetRelationResponse{}, err - } + err := s.store.DB().View(func(tx *bolt.Tx) error { + relations, err := bdb.Scan[dsc3.Relation](ctx, tx, bdb.RelationsObjPath, ds.GetRelation(req).ObjKey()) + if err != nil { + return err + } - results := make([]*dsc3.Relation, len(resp.Results)) - for i, r := range resp.Results { - results[i] = &dsc3.Relation{ - ObjectType: r.Object.GetType(), - ObjectId: r.Object.GetKey(), - Relation: r.Relation, - SubjectType: r.Subject.GetType(), - SubjectId: r.Subject.GetKey(), - SubjectRelation: "", - CreatedAt: r.CreatedAt, - UpdatedAt: r.UpdatedAt, - Etag: r.Hash, + if len(relations) == 0 { + return bdb.ErrKeyNotFound } - } + if len(relations) != 1 { + return bdb.ErrMultipleResults + } + + result := relations[0] + resp.Result = result - objects := make(map[string]*dsc3.Object, len(resp.Objects)) - for k, v := range resp.Objects { - objects[k] = &dsc3.Object{ - Type: v.Type, - Id: v.Key, - DisplayName: v.DisplayName, - Properties: v.Properties, - CreatedAt: v.CreatedAt, - UpdatedAt: v.UpdatedAt, - Etag: v.Hash, + if req.GetWithObjects() { + objects := map[string]*dsc3.Object{} + rel := ds.Relation(result) + + sub, err := bdb.Get[dsc3.Object](ctx, tx, bdb.ObjectsPath, ds.ObjectIdentifier(rel.Subject()).Key()) + if err != nil { + return err + } + objects[ds.Object(sub).Key()] = sub + + obj, err := bdb.Get[dsc3.Object](ctx, tx, bdb.ObjectsPath, ds.ObjectIdentifier(rel.Object()).Key()) + if err != nil { + return err + } + objects[ds.Object(obj).Key()] = obj + + resp.Objects = objects } - } - return &dsr3.GetRelationResponse{ - Result: results[0], - Objects: objects, - }, nil + return nil + }) + + return resp, err } +// GetRelations, gets paginated set of relation instances based on subject, relation, object filter. func (s *Reader) GetRelations(ctx context.Context, req *dsr3.GetRelationsRequest) (*dsr3.GetRelationsResponse, error) { + resp := &dsr3.GetRelationsResponse{Results: []*dsc3.Relation{}, Page: &dsc3.PaginationResponse{}} + if err := s.v.Validate(req); err != nil { - return &dsr3.GetRelationsResponse{}, err + return resp, err } - resp, err := s.r2.GetRelations(ctx, &dsr2.GetRelationsRequest{ - Param: &dsc2.RelationIdentifier{ - Object: &dsc2.ObjectIdentifier{ - Type: &req.ObjectType, - Key: &req.ObjectId, - }, - Relation: &dsc2.RelationTypeIdentifier{ - ObjectType: &req.ObjectType, - Name: &req.Relation, - }, - Subject: &dsc2.ObjectIdentifier{ - Type: &req.SubjectType, - Key: &req.SubjectId, - }, - }, - Page: ds.PaginationRequest2(req.Page), - }) + if req.Page == nil { + req.Page = &dsc3.PaginationRequest{Size: 100} + } - if err != nil { - return &dsr3.GetRelationsResponse{}, err + path, keyFilter, valueFilter := ds.GetRelations(req).Filter() + + opts := []bdb.ScanOption{ + bdb.WithPageToken(req.Page.Token), + bdb.WithKeyFilter(keyFilter), } - results := make([]*dsc3.Relation, len(resp.Results)) - for i, r := range resp.Results { - results[i] = &dsc3.Relation{ - ObjectType: r.Object.GetType(), - ObjectId: r.Object.GetKey(), - Relation: r.Relation, - SubjectType: r.Subject.GetType(), - SubjectId: r.Subject.GetKey(), - SubjectRelation: "", - CreatedAt: r.CreatedAt, - UpdatedAt: r.UpdatedAt, - Etag: r.Hash, + err := s.store.DB().View(func(tx *bolt.Tx) error { + iter, err := bdb.NewScanIterator[dsc3.Relation](ctx, tx, path, opts...) + if err != nil { + return err } - } - return &dsr3.GetRelationsResponse{ - Results: results, - Page: ds.PaginationResponse3(resp.Page), - }, nil + for iter.Next() { + if !valueFilter(iter.Value()) { + continue + } + resp.Results = append(resp.Results, iter.Value()) + + if req.Page.Size == int32(len(resp.Results)) { + if iter.Next() { + resp.Page.NextToken = iter.Key() + } + break + } + } + + return nil + }) + + return resp, err } -// check method. +// Check. func (s *Reader) Check(ctx context.Context, req *dsr3.CheckRequest) (*dsr3.CheckResponse, error) { + resp := &dsr3.CheckResponse{} + + if err := s.v.Validate(req); err != nil { + return resp, err + } + return nil, status.Error(codes.Unimplemented, "check function is not implemented") } -// check permission method. +// CheckPermission, check is subject granted permission on object. func (s *Reader) CheckPermission(ctx context.Context, req *dsr3.CheckPermissionRequest) (*dsr3.CheckPermissionResponse, error) { + resp := &dsr3.CheckPermissionResponse{} + if err := s.v.Validate(req); err != nil { - return &dsr3.CheckPermissionResponse{}, err + return resp, err } - resp, err := s.r2.CheckPermission(ctx, &dsr2.CheckPermissionRequest{ - Object: &dsc2.ObjectIdentifier{ - Type: &req.ObjectType, - Key: &req.ObjectId, - }, - Permission: &dsc2.PermissionIdentifier{ - Name: &req.Permission, - }, - Subject: &dsc2.ObjectIdentifier{ - Type: &req.SubjectType, - Key: &req.SubjectId, - }, + err := s.store.DB().View(func(tx *bolt.Tx) error { + var err error + resp, err = ds.CheckPermission(req).Exec(ctx, tx, s.store.MC()) + return err }) - if err != nil { - return &dsr3.CheckPermissionResponse{}, err - } - - return &dsr3.CheckPermissionResponse{ - Check: resp.Check, - Trace: resp.Trace, - }, nil + return resp, err } -// check relation method. +// CheckRelation check is subject has relation with object. func (s *Reader) CheckRelation(ctx context.Context, req *dsr3.CheckRelationRequest) (*dsr3.CheckRelationResponse, error) { + resp := &dsr3.CheckRelationResponse{} + if err := s.v.Validate(req); err != nil { return &dsr3.CheckRelationResponse{}, err } - resp, err := s.r2.CheckRelation(ctx, &dsr2.CheckRelationRequest{ - Object: &dsc2.ObjectIdentifier{ - Type: &req.ObjectType, - Key: &req.ObjectId, - }, - Relation: &dsc2.RelationTypeIdentifier{ - ObjectType: &req.ObjectType, - Name: &req.Relation, - }, - Subject: &dsc2.ObjectIdentifier{ - Type: &req.SubjectType, - Key: &req.SubjectId, - }, + err := s.store.DB().View(func(tx *bolt.Tx) error { + var err error + resp, err = ds.CheckRelation(req).Exec(ctx, tx, s.store.MC()) + return err }) - if err != nil { - return &dsr3.CheckRelationResponse{}, err - } - - return &dsr3.CheckRelationResponse{ - Check: resp.Check, - Trace: resp.Trace, - }, nil + return resp, err } -// graph methods. +// GetGraph. func (s *Reader) GetGraph(ctx context.Context, req *dsr3.GetGraphRequest) (*dsr3.GetGraphResponse, error) { - if err := s.v.Validate(req); err != nil { - return &dsr3.GetGraphResponse{}, err - } - - resp, err := s.r2.GetGraph(ctx, &dsr2.GetGraphRequest{ - Anchor: &dsc2.ObjectIdentifier{ - Type: &req.AnchorType, - Key: &req.AnchorId, - }, - Object: &dsc2.ObjectIdentifier{ - Type: &req.ObjectType, - Key: &req.ObjectId, - }, - Relation: &dsc2.RelationTypeIdentifier{ - ObjectType: &req.ObjectType, - Name: &req.Relation, - }, - Subject: &dsc2.ObjectIdentifier{ - Type: &req.SubjectType, - Key: &req.SubjectId, - }, - }) + resp := &dsr3.GetGraphResponse{} - if err != nil { + if err := s.v.Validate(req); err != nil { return &dsr3.GetGraphResponse{}, err } - results := make([]*dsc3.ObjectDependency, len(resp.Results)) - for i, r := range resp.Results { - results[i] = &dsc3.ObjectDependency{ - ObjectType: r.ObjectType, - ObjectId: r.ObjectKey, - Relation: r.Relation, - SubjectType: r.SubjectType, - SubjectId: r.SubjectKey, - SubjectRelation: "", - Depth: r.Depth, - IsCycle: r.IsCycle, - Path: r.Path, + err := s.store.DB().View(func(tx *bolt.Tx) error { + var err error + results, err := ds.GetGraph(req).Exec(ctx, tx) + if err != nil { + return err } - } - return &dsr3.GetGraphResponse{ - Results: results, - }, nil + resp.Results = results + return nil + }) + + return resp, err } diff --git a/pkg/directory/v3/writer.go b/pkg/directory/v3/writer.go index 6689753..f6c23e9 100644 --- a/pkg/directory/v3/writer.go +++ b/pkg/directory/v3/writer.go @@ -3,21 +3,21 @@ package v3 import ( "context" - dsc2 "github.com/aserto-dev/go-directory/aserto/directory/common/v2" dsc3 "github.com/aserto-dev/go-directory/aserto/directory/common/v3" - dsw2 "github.com/aserto-dev/go-directory/aserto/directory/writer/v2" dsw3 "github.com/aserto-dev/go-directory/aserto/directory/writer/v3" "github.com/aserto-dev/go-edge-ds/pkg/bdb" v2 "github.com/aserto-dev/go-edge-ds/pkg/directory/v2" - "github.com/bufbuild/protovalidate-go" + "github.com/aserto-dev/go-edge-ds/pkg/ds" + "google.golang.org/protobuf/types/known/emptypb" + "github.com/bufbuild/protovalidate-go" "github.com/rs/zerolog" + bolt "go.etcd.io/bbolt" ) type Writer struct { logger *zerolog.Logger store *bdb.BoltDB - w2 dsw2.WriterServer v *protovalidate.Validator } @@ -26,135 +26,160 @@ func NewWriter(logger *zerolog.Logger, store *bdb.BoltDB, w *v2.Writer) *Writer return &Writer{ logger: logger, store: store, - w2: w, v: v, } } // object methods. func (s *Writer) SetObject(ctx context.Context, req *dsw3.SetObjectRequest) (*dsw3.SetObjectResponse, error) { + resp := &dsw3.SetObjectResponse{} + if err := s.v.Validate(req); err != nil { - return &dsw3.SetObjectResponse{}, err + return resp, err } - resp, err := s.w2.SetObject(ctx, &dsw2.SetObjectRequest{ - Object: &dsc2.Object{ - Type: req.Object.Type, - Key: req.Object.Id, - DisplayName: req.Object.DisplayName, - Properties: req.Object.Properties, - CreatedAt: req.Object.CreatedAt, - UpdatedAt: req.Object.UpdatedAt, - Hash: req.Object.Etag, - }, - }) + req.Object.Etag = ds.Object(req.Object).Hash() - if err != nil { - return &dsw3.SetObjectResponse{}, err - } + err := s.store.DB().Update(func(tx *bolt.Tx) error { + updReq, err := bdb.UpdateMetadata(ctx, tx, bdb.ObjectsPath, ds.Object(req.Object).Key(), req.Object) + if err != nil { + return err + } + + objType, err := bdb.Set(ctx, tx, bdb.ObjectsPath, ds.Object(req.Object).Key(), updReq) + if err != nil { + return err + } - return &dsw3.SetObjectResponse{ - Result: &dsc3.Object{ - Type: resp.Result.Type, - Id: resp.Result.Key, - DisplayName: resp.Result.DisplayName, - Properties: resp.Result.Properties, - CreatedAt: resp.Result.CreatedAt, - UpdatedAt: resp.Result.UpdatedAt, - Etag: resp.Result.Hash, - }, - }, nil + resp.Result = objType + return nil + }) + + return resp, err } func (s *Writer) DeleteObject(ctx context.Context, req *dsw3.DeleteObjectRequest) (*dsw3.DeleteObjectResponse, error) { + resp := &dsw3.DeleteObjectResponse{} + if err := s.v.Validate(req); err != nil { - return &dsw3.DeleteObjectResponse{}, err + return resp, err } - resp, err := s.w2.DeleteObject(ctx, &dsw2.DeleteObjectRequest{ - Param: &dsc2.ObjectIdentifier{ - Type: &req.ObjectType, - Key: &req.ObjectId, - }, + err := s.store.DB().Update(func(tx *bolt.Tx) error { + objIdent := &dsc3.ObjectIdentifier{ObjectType: req.GetObjectType(), ObjectId: req.GetObjectId()} + if err := bdb.Delete(ctx, tx, bdb.ObjectsPath, ds.ObjectIdentifier(objIdent).Key()); err != nil { + return err + } + + if req.GetWithRelations() { + { + // incoming object relations of object instance (result.type == incoming.subject.type && result.key == incoming.subject.key) + iter, err := bdb.NewScanIterator[dsc3.Relation](ctx, tx, bdb.RelationsSubPath, bdb.WithKeyFilter(ds.ObjectIdentifier(objIdent).Key()+ds.InstanceSeparator)) + if err != nil { + return err + } + + rel := ds.Relation(iter.Value()) + for iter.Next() { + if err := bdb.Delete(ctx, tx, bdb.RelationsObjPath, rel.ObjKey()); err != nil { + return err + } + + if err := bdb.Delete(ctx, tx, bdb.RelationsSubPath, rel.SubKey()); err != nil { + return err + } + } + } + { + // outgoing object relations of object instance (result.type == outgoing.object.type && result.key == outgoing.object.key) + iter, err := bdb.NewScanIterator[dsc3.Relation](ctx, tx, bdb.RelationsObjPath, bdb.WithKeyFilter(ds.ObjectIdentifier(objIdent).Key()+ds.InstanceSeparator)) + if err != nil { + return err + } + + rel := ds.Relation(iter.Value()) + for iter.Next() { + if err := bdb.Delete(ctx, tx, bdb.RelationsObjPath, rel.ObjKey()); err != nil { + return err + } + + if err := bdb.Delete(ctx, tx, bdb.RelationsSubPath, rel.SubKey()); err != nil { + return err + } + } + } + } + + resp.Result = &emptypb.Empty{} + return nil }) - if err != nil { - return &dsw3.DeleteObjectResponse{}, err - } - - return &dsw3.DeleteObjectResponse{ - Result: resp.Result, - }, nil + return resp, err } // relation methods. func (s *Writer) SetRelation(ctx context.Context, req *dsw3.SetRelationRequest) (*dsw3.SetRelationResponse, error) { + resp := &dsw3.SetRelationResponse{} + if err := s.v.Validate(req); err != nil { - return &dsw3.SetRelationResponse{}, err + return resp, err } - resp, err := s.w2.SetRelation(ctx, &dsw2.SetRelationRequest{ - Relation: &dsc2.Relation{ - Object: &dsc2.ObjectIdentifier{ - Type: &req.Relation.ObjectType, - Key: &req.Relation.ObjectId, - }, - Relation: req.Relation.Relation, - Subject: &dsc2.ObjectIdentifier{ - Type: &req.Relation.SubjectType, - Key: &req.Relation.SubjectId, - }, - CreatedAt: req.Relation.CreatedAt, - UpdatedAt: req.Relation.UpdatedAt, - Hash: req.Relation.Etag, - }, - }) + req.Relation.Etag = ds.Relation(req.Relation).Hash() - if err != nil { - return &dsw3.SetRelationResponse{}, err - } + err := s.store.DB().Update(func(tx *bolt.Tx) error { + updReq, err := bdb.UpdateMetadata(ctx, tx, bdb.RelationsObjPath, ds.Relation(req.Relation).ObjKey(), req.Relation) + if err != nil { + return err + } + + objRel, err := bdb.Set(ctx, tx, bdb.RelationsObjPath, ds.Relation(req.Relation).ObjKey(), updReq) + if err != nil { + return err + } + + subRel, err := bdb.Set(ctx, tx, bdb.RelationsSubPath, ds.Relation(req.Relation).SubKey(), req.Relation) + if err != nil { + return err + } + + resp.Result = objRel + _ = subRel - return &dsw3.SetRelationResponse{ - Result: &dsc3.Relation{ - ObjectType: *resp.Result.Object.Type, - ObjectId: *resp.Result.Object.Key, - Relation: resp.Result.Relation, - SubjectType: *resp.Result.Subject.Type, - SubjectId: *resp.Result.Subject.Key, - CreatedAt: resp.Result.CreatedAt, - UpdatedAt: resp.Result.UpdatedAt, - Etag: resp.Result.Hash, - }, - }, nil + return nil + }) + + return resp, err } func (s *Writer) DeleteRelation(ctx context.Context, req *dsw3.DeleteRelationRequest) (*dsw3.DeleteRelationResponse, error) { + resp := &dsw3.DeleteRelationResponse{} + if err := s.v.Validate(req); err != nil { - return &dsw3.DeleteRelationResponse{}, err + return resp, err } - resp, err := s.w2.DeleteRelation(ctx, &dsw2.DeleteRelationRequest{ - Param: &dsc2.RelationIdentifier{ - Object: &dsc2.ObjectIdentifier{ - Type: &req.ObjectType, - Key: &req.ObjectId, - }, - Relation: &dsc2.RelationTypeIdentifier{ - ObjectType: &req.ObjectType, - Name: &req.Relation, - }, - Subject: &dsc2.ObjectIdentifier{ - Type: &req.SubjectType, - Key: &req.SubjectId, - }, - }, + err := s.store.DB().Update(func(tx *bolt.Tx) error { + rel := ds.Relation(&dsc3.Relation{ + ObjectType: req.ObjectType, + ObjectId: req.ObjectId, + Relation: req.Relation, + SubjectType: req.SubjectType, + SubjectId: req.SubjectId, + SubjectRelation: req.SubjectRelation, + }) + + if err := bdb.Delete(ctx, tx, bdb.RelationsObjPath, rel.ObjKey()); err != nil { + return err + } + + if err := bdb.Delete(ctx, tx, bdb.RelationsSubPath, rel.SubKey()); err != nil { + return err + } + + resp.Result = &emptypb.Empty{} + return nil }) - if err != nil { - return &dsw3.DeleteRelationResponse{}, err - } - - return &dsw3.DeleteRelationResponse{ - Result: resp.Result, - }, nil + return resp, err } diff --git a/pkg/ds/check_permission.go b/pkg/ds/check_permission.go index 4265122..6ad83be 100644 --- a/pkg/ds/check_permission.go +++ b/pkg/ds/check_permission.go @@ -5,8 +5,8 @@ import ( "github.com/aserto-dev/azm/cache" "github.com/aserto-dev/azm/model" - dsc2 "github.com/aserto-dev/go-directory/aserto/directory/common/v2" - dsr2 "github.com/aserto-dev/go-directory/aserto/directory/reader/v2" + dsc3 "github.com/aserto-dev/go-directory/aserto/directory/common/v3" + dsr3 "github.com/aserto-dev/go-directory/aserto/directory/reader/v3" "github.com/aserto-dev/go-edge-ds/pkg/bdb" "github.com/aserto-dev/go-edge-ds/pkg/pb" @@ -15,14 +15,28 @@ import ( ) type checkPermission struct { - *dsr2.CheckPermissionRequest + *dsr3.CheckPermissionRequest } -func CheckPermission(i *dsr2.CheckPermissionRequest) *checkPermission { +func CheckPermission(i *dsr3.CheckPermissionRequest) *checkPermission { return &checkPermission{i} } -func (i *checkPermission) Validate() (bool, error) { +func (i *checkPermission) Object() *dsc3.ObjectIdentifier { + return &dsc3.ObjectIdentifier{ + ObjectType: i.ObjectType, + ObjectId: i.ObjectId, + } +} + +func (i *checkPermission) Subject() *dsc3.ObjectIdentifier { + return &dsc3.ObjectIdentifier{ + ObjectType: i.SubjectType, + ObjectId: i.SubjectId, + } +} + +func (i *checkPermission) Validate(mc *cache.Cache) (bool, error) { if i == nil { return false, ErrInvalidArgumentObjectType.Msg("check permission request not set (nil)") } @@ -31,40 +45,40 @@ func (i *checkPermission) Validate() (bool, error) { return false, ErrInvalidArgumentObjectType.Msg("check permission request not set (nil)") } - if ok, err := ObjectIdentifier(i.CheckPermissionRequest.Object).Validate(); !ok { + if ok, err := ObjectIdentifier(i.Object()).Validate(); !ok { return ok, err } - if ok, err := PermissionIdentifier(i.CheckPermissionRequest.Permission).Validate(); !ok { - return ok, err + if !mc.PermissionExists(model.ObjectName(i.ObjectType), model.PermissionName(i.Permission)) { + return false, ErrPermissionNotFound.Msgf("%s%s%s", i.ObjectType, RelationSeparator, i.Permission) } - if ok, err := ObjectIdentifier(i.CheckPermissionRequest.Subject).Validate(); !ok { + if ok, err := ObjectIdentifier(i.Subject()).Validate(); !ok { return ok, err } return true, nil } -func (i *checkPermission) Exec(ctx context.Context, tx *bolt.Tx, mc *cache.Cache) (*dsr2.CheckPermissionResponse, error) { - resp := &dsr2.CheckPermissionResponse{Check: false, Trace: []string{}} +func (i *checkPermission) Exec(ctx context.Context, tx *bolt.Tx, mc *cache.Cache) (*dsr3.CheckPermissionResponse, error) { + resp := &dsr3.CheckPermissionResponse{Check: false, Trace: []string{}} check, err := i.newChecker(ctx, tx, bdb.RelationsObjPath, mc) if err != nil { return resp, err } - match, err := check.check(i.Object) + match, err := check.check(i.Object()) - return &dsr2.CheckPermissionResponse{Check: match}, err + return &dsr3.CheckPermissionResponse{Check: match}, err } func (i *checkPermission) newChecker(ctx context.Context, tx *bolt.Tx, path []string, mc *cache.Cache) (*permissionChecker, error) { relations := mc.ExpandPermission( - model.ObjectName(i.CheckPermissionRequest.Object.GetType()), - model.PermissionName(i.CheckPermissionRequest.Permission.GetName())) + model.ObjectName(i.GetObjectType()), + model.PermissionName(i.GetPermission())) - userSet, err := CreateUserSet(ctx, tx, i.Subject) + userSet, err := CreateUserSet(ctx, tx, i.Subject()) if err != nil { return nil, err } @@ -76,7 +90,7 @@ func (i *checkPermission) newChecker(ctx context.Context, tx *bolt.Tx, path []st anchor: i, userSet: userSet, filter: relations, - trace: [][]*dsc2.Relation{}, + trace: [][]*dsc3.Relation{}, }, nil } @@ -85,15 +99,15 @@ type permissionChecker struct { tx *bolt.Tx path []string anchor *checkPermission - userSet []*dsc2.ObjectIdentifier + userSet []*dsc3.ObjectIdentifier filter []model.RelationName - trace [][]*dsc2.Relation + trace [][]*dsc3.Relation } -func (c *permissionChecker) check(root *dsc2.ObjectIdentifier) (bool, error) { +func (c *permissionChecker) check(root *dsc3.ObjectIdentifier) (bool, error) { // relations associated to object instance. filter := ObjectIdentifier(root).Key() + InstanceSeparator - relations, err := bdb.Scan[dsc2.Relation](c.ctx, c.tx, c.path, filter) + relations, err := bdb.Scan[dsc3.Relation](c.ctx, c.tx, c.path, filter) if err != nil { return false, err } @@ -106,7 +120,7 @@ func (c *permissionChecker) check(root *dsc2.ObjectIdentifier) (bool, error) { for _, r := range relations { if lo.Contains(c.filter, model.RelationName(r.Relation)) || r.Relation == "parent" { - match, err := c.check(r.Subject) + match, err := c.check(Relation(r).Subject()) if err != nil { return false, err } @@ -120,8 +134,8 @@ func (c *permissionChecker) check(root *dsc2.ObjectIdentifier) (bool, error) { return false, nil } -func (c *permissionChecker) isMatch(relation *dsc2.Relation) bool { - if lo.Contains(c.filter, model.RelationName(relation.Relation)) && pb.Contains[*dsc2.ObjectIdentifier](c.userSet, relation.Subject) { +func (c *permissionChecker) isMatch(relation *dsc3.Relation) bool { + if lo.Contains(c.filter, model.RelationName(relation.Relation)) && pb.Contains[*dsc3.ObjectIdentifier](c.userSet, Relation(relation).Subject()) { return true } return false diff --git a/pkg/ds/check_relation.go b/pkg/ds/check_relation.go index 0b78e4e..03a089a 100644 --- a/pkg/ds/check_relation.go +++ b/pkg/ds/check_relation.go @@ -5,8 +5,8 @@ import ( "github.com/aserto-dev/azm/cache" "github.com/aserto-dev/azm/model" - dsc2 "github.com/aserto-dev/go-directory/aserto/directory/common/v2" - dsr2 "github.com/aserto-dev/go-directory/aserto/directory/reader/v2" + dsc3 "github.com/aserto-dev/go-directory/aserto/directory/common/v3" + dsr3 "github.com/aserto-dev/go-directory/aserto/directory/reader/v3" "github.com/aserto-dev/go-edge-ds/pkg/bdb" "github.com/aserto-dev/go-edge-ds/pkg/pb" @@ -15,14 +15,28 @@ import ( ) type checkRelation struct { - *dsr2.CheckRelationRequest + *dsr3.CheckRelationRequest } -func CheckRelation(i *dsr2.CheckRelationRequest) *checkRelation { +func CheckRelation(i *dsr3.CheckRelationRequest) *checkRelation { return &checkRelation{i} } -func (i *checkRelation) Validate() (bool, error) { +func (i *checkRelation) Object() *dsc3.ObjectIdentifier { + return &dsc3.ObjectIdentifier{ + ObjectType: i.ObjectType, + ObjectId: i.ObjectId, + } +} + +func (i *checkRelation) Subject() *dsc3.ObjectIdentifier { + return &dsc3.ObjectIdentifier{ + ObjectType: i.SubjectType, + ObjectId: i.SubjectId, + } +} + +func (i *checkRelation) Validate(mc *cache.Cache) (bool, error) { if i == nil { return false, ErrInvalidArgumentObjectType.Msg("check relation request not set (nil)") } @@ -31,45 +45,40 @@ func (i *checkRelation) Validate() (bool, error) { return false, ErrInvalidArgumentObjectType.Msg("check relations request not set (nil)") } - if ok, err := ObjectIdentifier(i.CheckRelationRequest.Object).Validate(); !ok { + if ok, err := ObjectIdentifier(i.Object()).Validate(); !ok { return ok, err } - if ok, err := ObjectIdentifier(i.CheckRelationRequest.Subject).Validate(); !ok { + if ok, err := ObjectIdentifier(i.Subject()).Validate(); !ok { return ok, err } -RECHECK: - if ok, err := RelationTypeIdentifier(i.CheckRelationRequest.Relation).Validate(); !ok { - if i.CheckRelationRequest.Relation.GetObjectType() == "" { - i.CheckRelationRequest.Relation.ObjectType = i.CheckRelationRequest.Object.Type - goto RECHECK - } - return ok, err + if !mc.RelationExists(model.ObjectName(i.ObjectType), model.RelationName(i.Relation)) { + return false, ErrRelationNotFound.Msgf("%s%s%s", i.ObjectType, RelationSeparator, i.Relation) } return true, nil } -func (i *checkRelation) Exec(ctx context.Context, tx *bolt.Tx, mc *cache.Cache) (*dsr2.CheckRelationResponse, error) { - resp := &dsr2.CheckRelationResponse{Check: false, Trace: []string{}} +func (i *checkRelation) Exec(ctx context.Context, tx *bolt.Tx, mc *cache.Cache) (*dsr3.CheckRelationResponse, error) { + resp := &dsr3.CheckRelationResponse{Check: false, Trace: []string{}} check, err := i.newChecker(ctx, tx, bdb.RelationsObjPath, mc) if err != nil { return resp, err } - match, err := check.check(i.Object) + match, err := check.check(i.Object()) - return &dsr2.CheckRelationResponse{Check: match}, err + return &dsr3.CheckRelationResponse{Check: match}, err } func (i *checkRelation) newChecker(ctx context.Context, tx *bolt.Tx, path []string, mc *cache.Cache) (*relationChecker, error) { relations := mc.ExpandRelation( - model.ObjectName(i.CheckRelationRequest.Object.GetType()), - model.RelationName(i.CheckRelationRequest.Relation.GetName())) + model.ObjectName(i.GetObjectType()), + model.RelationName(i.GetRelation())) - userSet, err := CreateUserSet(ctx, tx, i.Subject) + userSet, err := CreateUserSet(ctx, tx, i.Subject()) if err != nil { return nil, err } @@ -81,7 +90,7 @@ func (i *checkRelation) newChecker(ctx context.Context, tx *bolt.Tx, path []stri anchor: i, userSet: userSet, filter: relations, - trace: [][]*dsc2.Relation{}, + trace: [][]*dsc3.Relation{}, }, nil } @@ -90,16 +99,16 @@ type relationChecker struct { tx *bolt.Tx path []string anchor *checkRelation - userSet []*dsc2.ObjectIdentifier + userSet []*dsc3.ObjectIdentifier filter []model.RelationName - trace [][]*dsc2.Relation + trace [][]*dsc3.Relation } -func (c *relationChecker) check(root *dsc2.ObjectIdentifier) (bool, error) { +func (c *relationChecker) check(root *dsc3.ObjectIdentifier) (bool, error) { filter := ObjectIdentifier(root).Key() + InstanceSeparator // relations associated to object instance. - relations, err := bdb.Scan[dsc2.Relation](c.ctx, c.tx, c.path, filter) + relations, err := bdb.Scan[dsc3.Relation](c.ctx, c.tx, c.path, filter) if err != nil { return false, err } @@ -112,7 +121,7 @@ func (c *relationChecker) check(root *dsc2.ObjectIdentifier) (bool, error) { for _, r := range relations { if lo.Contains(c.filter, model.RelationName(r.Relation)) { - match, err := c.check(r.Subject) + match, err := c.check(Relation(r).Subject()) if err != nil { return false, err } @@ -126,8 +135,8 @@ func (c *relationChecker) check(root *dsc2.ObjectIdentifier) (bool, error) { return false, nil } -func (c *relationChecker) isMatch(relation *dsc2.Relation) bool { - if lo.Contains(c.filter, model.RelationName(relation.Relation)) && pb.Contains[*dsc2.ObjectIdentifier](c.userSet, relation.Subject) { +func (c *relationChecker) isMatch(relation *dsc3.Relation) bool { + if lo.Contains(c.filter, model.RelationName(relation.Relation)) && pb.Contains[*dsc3.ObjectIdentifier](c.userSet, Relation(relation).Subject()) { return true } return false diff --git a/pkg/ds/const.go b/pkg/ds/const.go index 533e39f..487acbf 100644 --- a/pkg/ds/const.go +++ b/pkg/ds/const.go @@ -3,6 +3,7 @@ package ds const ( TypeIDSeparator string = ":" InstanceSeparator string = "|" + RelationSeparator string = "#" ) const ( diff --git a/pkg/ds/error.go b/pkg/ds/error.go index 30d49e6..5ba8bda 100644 --- a/pkg/ds/error.go +++ b/pkg/ds/error.go @@ -10,10 +10,12 @@ import ( var ( // TODO renumber errors. - ErrObjectTypeNotFound = cerr.NewAsertoError("E20031", codes.NotFound, http.StatusNotFound, "object type not found") - ErrObjectNotFound = cerr.NewAsertoError("E20032", codes.NotFound, http.StatusNotFound, "object not found") - ErrRelationNotFound = cerr.NewAsertoError("E20033", codes.NotFound, http.StatusNotFound, "relation not found") - ErrRelationTypeNotFound = cerr.NewAsertoError("E20033", codes.NotFound, http.StatusNotFound, "relation type not found") + ErrObjectTypeNotFound = cerr.NewAsertoError("E20031", codes.NotFound, http.StatusNotFound, "object type not found") + ErrObjectNotFound = cerr.NewAsertoError("E20032", codes.NotFound, http.StatusNotFound, "object not found") + ErrRelationNotFound = cerr.NewAsertoError("E20033", codes.NotFound, http.StatusNotFound, "relation not found") + ErrRelationTypeNotFound = cerr.NewAsertoError("E20033", codes.NotFound, http.StatusNotFound, "relation type not found") + ErrPermissionNotFound = cerr.NewAsertoError("E20033", codes.NotFound, http.StatusNotFound, "permission not found") + ErrInvalidArgumentObjectTypeIdentifier = cerr.NewAsertoError("E20034", codes.InvalidArgument, http.StatusBadRequest, "object type identifier invalid argument") ErrInvalidArgumentRelationTypeIdentifier = cerr.NewAsertoError("E20034", codes.InvalidArgument, http.StatusBadRequest, "relation type identifier invalid argument") ErrInvalidArgumentObjectIdentifier = cerr.NewAsertoError("E20035", codes.InvalidArgument, http.StatusBadRequest, "object identifier invalid argument") diff --git a/pkg/ds/graph.go b/pkg/ds/graph.go index 717f464..8e63fcd 100644 --- a/pkg/ds/graph.go +++ b/pkg/ds/graph.go @@ -4,8 +4,10 @@ import ( "context" "strings" - dsc2 "github.com/aserto-dev/go-directory/aserto/directory/common/v2" - dsr2 "github.com/aserto-dev/go-directory/aserto/directory/reader/v2" + "github.com/aserto-dev/azm/cache" + "github.com/aserto-dev/azm/model" + dsc3 "github.com/aserto-dev/go-directory/aserto/directory/common/v3" + dsr3 "github.com/aserto-dev/go-directory/aserto/directory/reader/v3" "github.com/aserto-dev/go-directory/pkg/derr" "github.com/aserto-dev/go-edge-ds/pkg/bdb" @@ -14,74 +16,82 @@ import ( ) type getGraph struct { - *dsr2.GetGraphRequest + *dsr3.GetGraphRequest } -func GetGraph(i *dsr2.GetGraphRequest) *getGraph { +func GetGraph(i *dsr3.GetGraphRequest) *getGraph { return &getGraph{i} } -func (i *getGraph) Validate() (bool, error) { - if i == nil || i.GetGraphRequest == nil { - return false, ErrInvalidArgumentObjectType.Msg("get graph request not set (nil)") +func (i *getGraph) Anchor() *dsc3.ObjectIdentifier { + return &dsc3.ObjectIdentifier{ + ObjectType: i.AnchorType, + ObjectId: i.AnchorId, } +} - // anchor must be defined, hence use an ObjectIdentifier. - if ok, err := ObjectIdentifier(i.GetGraphRequest.Anchor).Validate(); !ok { - return ok, err +func (i *getGraph) Object() *dsc3.ObjectIdentifier { + return &dsc3.ObjectIdentifier{ + ObjectType: i.ObjectType, + ObjectId: i.ObjectId, } +} - // ensure object param block is initialized. - if i.GetGraphRequest.Object == nil { - i.GetGraphRequest.Object = &dsc2.ObjectIdentifier{} +func (i *getGraph) Subject() *dsc3.ObjectIdentifier { + return &dsc3.ObjectIdentifier{ + ObjectType: i.SubjectType, + ObjectId: i.SubjectId, } +} - // Object can be optional, hence the use of an ObjectSelector. - if ok, err := ObjectSelector(i.GetGraphRequest.Object).Validate(); !ok { - return ok, err +func (i *getGraph) Validate(mc *cache.Cache) (bool, error) { + if i == nil || i.GetGraphRequest == nil { + return false, ErrInvalidArgumentObjectType.Msg("get graph request not set (nil)") } - // ensure the relation param block is initialized. - if i.GetGraphRequest.Relation == nil { - i.GetGraphRequest.Relation = &dsc2.RelationTypeIdentifier{} + // anchor must be defined, hence use an ObjectIdentifier. + if ok, err := ObjectIdentifier(i.Anchor()).Validate(); !ok { + return ok, err } - // Relation can be optional, hence the use of a RelationTypeSelector. - if ok, err := RelationTypeSelector(i.GetGraphRequest.Relation).Validate(); !ok { + // Object can be optional, hence the use of an ObjectSelector. + if ok, err := ObjectSelector(i.Object()).Validate(); !ok { return ok, err } - // ensure the subject param block is initialized. - if i.GetGraphRequest.Subject == nil { - i.GetGraphRequest.Subject = &dsc2.ObjectIdentifier{} + // Relation can be optional, hence the use of a RelationTypeSelector. + if i.GetRelation() != "" { + if !mc.RelationExists(model.ObjectName(i.ObjectType), model.RelationName(i.Relation)) { + return false, ErrRelationNotFound.Msgf("%s%s%s", i.ObjectType, RelationSeparator, i.Relation) + } } // Subject can be option, hence the use of an ObjectSelector. - if ok, err := ObjectSelector(i.GetGraphRequest.Subject).Validate(); !ok { + if ok, err := ObjectSelector(i.Subject()).Validate(); !ok { return ok, err } // either Object or Subject must be equal to the Anchor to indicate the directionality of the graph walk. // Anchor == Subject ==> subject->object (this was the default and only directionality before enabling bi-directionality) // Anchor == Object ==> object->subject - if !ObjectIdentifier(i.GetGraphRequest.Anchor).Equal(i.GetGraphRequest.GetObject()) && - !ObjectIdentifier(i.GetGraphRequest.Anchor).Equal(i.GetGraphRequest.GetSubject()) { + if !ObjectIdentifier(i.Anchor()).Equal(i.Object()) && + !ObjectIdentifier(i.Anchor()).Equal(i.Subject()) { return false, ErrGraphDirectionality } return true, nil } -func (i *getGraph) Exec(ctx context.Context, tx *bolt.Tx /*, resolver *cache.Cache*/) ([]*dsc2.ObjectDependency, error) { - resp := []*dsc2.ObjectDependency{} +func (i *getGraph) Exec(ctx context.Context, tx *bolt.Tx /*, resolver *cache.Cache*/) ([]*dsc3.ObjectDependency, error) { + resp := []*dsc3.ObjectDependency{} // determine graph walk directionality. // Anchor == Subject ==> subject -> object // Anchor == Object ==> object -> subject var direction Direction - if ObjectIdentifier(i.Anchor).Equal(i.Subject) { + if ObjectIdentifier(i.Anchor()).Equal(i.Subject()) { direction = SubjectToObject - } else if ObjectIdentifier(i.Anchor).Equal(i.Object) { + } else if ObjectIdentifier(i.Anchor()).Equal(i.Object()) { direction = ObjectToSubject } else { return resp, ErrGraphDirectionality @@ -113,8 +123,8 @@ type GraphWalker struct { bucketPath []string direction Direction err error - req *dsr2.GetGraphRequest - results []*dsc2.ObjectDependency + req *dsr3.GetGraphRequest + results []*dsc3.ObjectDependency } func (i *getGraph) newGraphWalker(ctx context.Context, tx *bolt.Tx, direction Direction) *GraphWalker { @@ -123,7 +133,7 @@ func (i *getGraph) newGraphWalker(ctx context.Context, tx *bolt.Tx, direction Di tx: tx, direction: direction, req: i.GetGraphRequest, - results: []*dsc2.ObjectDependency{}, + results: []*dsc3.ObjectDependency{}, } } @@ -136,7 +146,7 @@ func (w *GraphWalker) Fetch() error { w.bucketPath = bdb.RelationsObjPath } - if err := w.walk(w.req.Anchor, 0, []string{}); err != nil { + if err := w.walk(GetGraph(w.req).Anchor(), 0, []string{}); err != nil { return err } @@ -144,51 +154,51 @@ func (w *GraphWalker) Fetch() error { } func (w *GraphWalker) Filter() error { - filters := []func(item *dsc2.ObjectDependency) bool{} + filters := []func(item *dsc3.ObjectDependency) bool{} // SubjectToObject: subject == anchor => filter on object & relation. if w.direction == SubjectToObject { - if w.req.Object.GetType() != "" { - filters = append(filters, func(item *dsc2.ObjectDependency) bool { - return strings.EqualFold(item.GetObjectType(), w.req.Object.GetType()) + if w.req.GetObjectType() != "" { + filters = append(filters, func(item *dsc3.ObjectDependency) bool { + return strings.EqualFold(item.GetObjectType(), w.req.GetObjectType()) }) } - if w.req.Object.GetKey() != "" { - filters = append(filters, func(item *dsc2.ObjectDependency) bool { - return strings.EqualFold(item.GetObjectKey(), w.req.Object.GetKey()) + if w.req.GetObjectId() != "" { + filters = append(filters, func(item *dsc3.ObjectDependency) bool { + return strings.EqualFold(item.GetObjectId(), w.req.GetObjectId()) }) } - if w.req.Relation.GetName() != "" { - filters = append(filters, func(item *dsc2.ObjectDependency) bool { - return strings.EqualFold(item.GetRelation(), w.req.Relation.GetName()) + if w.req.GetRelation() != "" { + filters = append(filters, func(item *dsc3.ObjectDependency) bool { + return strings.EqualFold(item.GetRelation(), w.req.GetRelation()) }) } } // ObjectToSubject: object == anchor => filter on subject & relation. if w.direction == ObjectToSubject { - if w.req.Subject.GetType() != "" { - filters = append(filters, func(item *dsc2.ObjectDependency) bool { - return strings.EqualFold(item.GetSubjectType(), w.req.Subject.GetType()) + if w.req.GetSubjectType() != "" { + filters = append(filters, func(item *dsc3.ObjectDependency) bool { + return strings.EqualFold(item.GetSubjectType(), w.req.GetSubjectType()) }) } - if w.req.Subject.GetKey() != "" { - filters = append(filters, func(item *dsc2.ObjectDependency) bool { - return strings.EqualFold(item.GetSubjectKey(), w.req.Subject.GetKey()) + if w.req.GetSubjectId() != "" { + filters = append(filters, func(item *dsc3.ObjectDependency) bool { + return strings.EqualFold(item.GetSubjectId(), w.req.GetSubjectId()) }) } - if w.req.Relation.GetName() != "" { - filters = append(filters, func(item *dsc2.ObjectDependency) bool { - return strings.EqualFold(item.GetRelation(), w.req.Relation.GetName()) + if w.req.GetRelation() != "" { + filters = append(filters, func(item *dsc3.ObjectDependency) bool { + return strings.EqualFold(item.GetRelation(), w.req.GetRelation()) }) } } - w.results = lo.Filter[*dsc2.ObjectDependency](w.results, func(item *dsc2.ObjectDependency, index int) bool { + w.results = lo.Filter[*dsc3.ObjectDependency](w.results, func(item *dsc3.ObjectDependency, index int) bool { for _, filter := range filters { if !filter(item) { return false @@ -200,22 +210,22 @@ func (w *GraphWalker) Filter() error { return nil } -func (w *GraphWalker) Results() ([]*dsc2.ObjectDependency, error) { +func (w *GraphWalker) Results() ([]*dsc3.ObjectDependency, error) { return w.results, w.err } -func (w *GraphWalker) walk(anchor *dsc2.ObjectIdentifier, depth int32, path []string) error { +func (w *GraphWalker) walk(anchor *dsc3.ObjectIdentifier, depth int32, path []string) error { depth++ if depth > maxDepth { - w.results = []*dsc2.ObjectDependency{} + w.results = []*dsc3.ObjectDependency{} w.err = derr.ErrMaxDepthExceeded return w.err } filter := ObjectIdentifier(anchor).Key() + InstanceSeparator - relations, err := bdb.Scan[dsc2.Relation](w.ctx, w.tx, w.bucketPath, filter) + relations, err := bdb.Scan[dsc3.Relation](w.ctx, w.tx, w.bucketPath, filter) if err != nil { return err } @@ -225,25 +235,29 @@ func (w *GraphWalker) walk(anchor *dsc2.ObjectIdentifier, depth int32, path []st p := make([]string, len(path)) copy(p, path) - p = append(p, rel.GetObject().GetType()+ + p = append(p, rel.GetObjectType()+ TypeIDSeparator+ - rel.GetObject().GetKey()+ + rel.GetObjectId()+ InstanceSeparator+ rel.GetRelation()+ InstanceSeparator+ - rel.GetSubject().GetType()+ + rel.GetSubjectType()+ TypeIDSeparator+ - rel.GetSubject().GetKey()) - - dep := dsc2.ObjectDependency{ - ObjectType: rel.GetObject().GetType(), - ObjectKey: rel.GetObject().GetKey(), - Relation: rel.Relation, - SubjectType: rel.GetSubject().GetType(), - SubjectKey: rel.GetObject().GetKey(), - Depth: depth, - IsCycle: false, - Path: p, + rel.GetSubjectId()+ + RelationSeparator+ + rel.GetSubjectRelation(), + ) + + dep := dsc3.ObjectDependency{ + ObjectType: rel.GetObjectType(), + ObjectId: rel.GetObjectId(), + Relation: rel.GetRelation(), + SubjectType: rel.GetSubjectType(), + SubjectId: rel.GetSubjectId(), + SubjectRelation: rel.GetSubjectRelation(), + Depth: depth, + IsCycle: false, + Path: p, } w.results = append(w.results, &dep) @@ -255,9 +269,9 @@ func (w *GraphWalker) walk(anchor *dsc2.ObjectIdentifier, depth int32, path []st return nil } -func (w *GraphWalker) next(r *dsc2.Relation) *dsc2.ObjectIdentifier { +func (w *GraphWalker) next(r *dsc3.Relation) *dsc3.ObjectIdentifier { if w.direction == ObjectToSubject { - return r.GetSubject() + return Relation(r).Subject() } - return r.GetObject() + return Relation(r).Object() } diff --git a/pkg/ds/object.go b/pkg/ds/object.go index 4eab76f..f11b3ac 100644 --- a/pkg/ds/object.go +++ b/pkg/ds/object.go @@ -7,12 +7,11 @@ import ( "github.com/aserto-dev/azm/cache" "github.com/aserto-dev/azm/model" - dsc2 "github.com/aserto-dev/go-directory/aserto/directory/common/v2" + dsc3 "github.com/aserto-dev/go-directory/aserto/directory/common/v3" "github.com/aserto-dev/go-directory/pkg/derr" "github.com/aserto-dev/go-edge-ds/pkg/pb" "github.com/mitchellh/hashstructure/v2" - "google.golang.org/protobuf/proto" ) // model contains object related items. @@ -23,13 +22,13 @@ const ( ) type object struct { - *dsc2.Object + *dsc3.Object } -func Object(i *dsc2.Object) *object { return &object{i} } +func Object(i *dsc3.Object) *object { return &object{i} } func (i *object) Key() string { - return i.GetType() + TypeIDSeparator + i.GetKey() + return i.GetType() + TypeIDSeparator + i.GetId() } func (i *object) Validate(mc *cache.Cache) (bool, error) { @@ -42,8 +41,8 @@ func (i *object) Validate(mc *cache.Cache) (bool, error) { return false, ErrInvalidArgumentObject.Msg(objectIdentifierType) } - // #2 check if key field is set. - if IsNotSet(i.GetKey()) { + // #2 check if id field is set. + if IsNotSet(i.GetId()) { return false, ErrInvalidArgumentObjectIdentifier.Msg(objectIdentifierKey) } @@ -84,7 +83,7 @@ func (i *object) Hash() string { if _, err := h.Write([]byte(i.GetType())); err != nil { return DefaultHash } - if _, err := h.Write([]byte(i.GetKey())); err != nil { + if _, err := h.Write([]byte(i.GetId())); err != nil { return DefaultHash } @@ -96,16 +95,16 @@ func (i *object) Hash() string { } type objectIdentifier struct { - *dsc2.ObjectIdentifier + *dsc3.ObjectIdentifier } -func ObjectIdentifier(i *dsc2.ObjectIdentifier) *objectIdentifier { return &objectIdentifier{i} } +func ObjectIdentifier(i *dsc3.ObjectIdentifier) *objectIdentifier { return &objectIdentifier{i} } // TODO not used, integrated into validate or set. -func (i *objectIdentifier) Normalize() { - i.ObjectIdentifier.Key = proto.String(strings.ToLower(strings.TrimSpace(i.GetKey()))) - i.ObjectIdentifier.Type = proto.String(strings.ToLower(strings.TrimSpace(i.GetType()))) -} +// func (i *objectIdentifier) Normalize() { +// i.ObjectIdentifier.Type = proto.String(strings.ToLower(strings.TrimSpace(i.GetType()))) +// i.ObjectIdentifier.Key = proto.String(strings.ToLower(strings.TrimSpace(i.GetKey()))) +// } func (i *objectIdentifier) Validate() (bool, error) { if i.ObjectIdentifier == nil { @@ -113,12 +112,12 @@ func (i *objectIdentifier) Validate() (bool, error) { } // #1 check is type field is set. - if IsNotSet(i.GetType()) { + if IsNotSet(i.GetObjectType()) { return false, ErrInvalidArgumentObjectIdentifier.Msg(objectIdentifierType) } - // #2 check if key field is set. - if IsNotSet(i.GetKey()) { + // #2 check if id field is set. + if IsNotSet(i.GetObjectId()) { return false, ErrInvalidArgumentObjectIdentifier.Msg(objectIdentifierKey) } @@ -129,27 +128,27 @@ func (i *objectIdentifier) Validate() (bool, error) { } func (i *objectIdentifier) Key() string { - return i.GetType() + TypeIDSeparator + i.GetKey() + return i.GetObjectType() + TypeIDSeparator + i.GetObjectId() } -func (i *objectIdentifier) Equal(n *dsc2.ObjectIdentifier) bool { - return strings.EqualFold(i.ObjectIdentifier.GetKey(), n.GetKey()) && strings.EqualFold(i.ObjectIdentifier.GetType(), n.GetType()) +func (i *objectIdentifier) Equal(n *dsc3.ObjectIdentifier) bool { + return strings.EqualFold(i.ObjectIdentifier.GetObjectId(), n.GetObjectId()) && strings.EqualFold(i.ObjectIdentifier.GetObjectType(), n.GetObjectType()) } func (i *objectIdentifier) IsComplete() bool { - return i != nil && i.GetType() != "" && i.GetKey() != "" + return i != nil && i.GetObjectType() != "" && i.GetObjectId() != "" } type objectSelector struct { - *dsc2.ObjectIdentifier + *dsc3.ObjectIdentifier } -func ObjectSelector(i *dsc2.ObjectIdentifier) *objectSelector { return &objectSelector{i} } +func ObjectSelector(i *dsc3.ObjectIdentifier) *objectSelector { return &objectSelector{i} } -func (i *objectSelector) Normalize() { - i.Key = proto.String(strings.ToLower(strings.TrimSpace(i.GetKey()))) - i.Type = proto.String(strings.ToLower(strings.TrimSpace(i.GetType()))) -} +// func (i *objectSelector) Normalize() { +// i.Key = proto.String(strings.ToLower(strings.TrimSpace(i.GetKey()))) +// i.Type = proto.String(strings.ToLower(strings.TrimSpace(i.GetType()))) +// } // Validate rules: // valid states @@ -163,17 +162,17 @@ func (i *objectSelector) Validate() (bool, error) { } // empty object - if IsNotSet(i.GetType()) && IsNotSet(i.GetKey()) { + if IsNotSet(i.GetObjectType()) && IsNotSet(i.GetObjectId()) { return true, nil } // type only - if IsSet(i.GetType()) && IsNotSet(i.GetKey()) { + if IsSet(i.GetObjectType()) && IsNotSet(i.GetObjectId()) { return true, nil } // type + key - if IsSet(i.GetType()) && IsSet(i.GetKey()) { + if IsSet(i.GetObjectType()) && IsSet(i.GetObjectId()) { return true, nil } @@ -181,5 +180,5 @@ func (i *objectSelector) Validate() (bool, error) { } func (i *objectSelector) IsComplete() bool { - return IsSet(i.GetType()) && IsSet(i.GetKey()) + return IsSet(i.GetObjectType()) && IsSet(i.GetObjectId()) } diff --git a/pkg/ds/relation.go b/pkg/ds/relation.go index 62037cf..436be69 100644 --- a/pkg/ds/relation.go +++ b/pkg/ds/relation.go @@ -9,39 +9,80 @@ import ( "github.com/aserto-dev/azm/cache" "github.com/aserto-dev/azm/model" - dsc2 "github.com/aserto-dev/go-directory/aserto/directory/common/v2" + dsc3 "github.com/aserto-dev/go-directory/aserto/directory/common/v3" + dsr3 "github.com/aserto-dev/go-directory/aserto/directory/reader/v3" "github.com/aserto-dev/go-directory/pkg/derr" "github.com/aserto-dev/go-edge-ds/pkg/bdb" - "github.com/pkg/errors" "github.com/rs/zerolog/log" ) // Relation. type relation struct { - *dsc2.Relation + *dsc3.Relation } -func Relation(i *dsc2.Relation) *relation { return &relation{i} } +func Relation(i *dsc3.Relation) *relation { return &relation{i} } + +func GetRelation(i *dsr3.GetRelationRequest) *relation { + return &relation{&dsc3.Relation{ + ObjectType: i.ObjectType, + ObjectId: i.ObjectId, + Relation: i.Relation, + SubjectType: i.SubjectType, + SubjectId: i.SubjectId, + SubjectRelation: i.SubjectRelation, + }} +} + +func GetRelations(i *dsr3.GetRelationsRequest) *relation { + return &relation{&dsc3.Relation{ + ObjectType: i.ObjectType, + ObjectId: i.ObjectId, + Relation: i.Relation, + SubjectType: i.SubjectType, + SubjectId: i.SubjectId, + SubjectRelation: i.SubjectRelation, + }} +} func (i *relation) Key() string { return i.ObjKey() } +func (i *relation) Object() *dsc3.ObjectIdentifier { + return &dsc3.ObjectIdentifier{ + ObjectType: i.GetObjectType(), + ObjectId: i.GetObjectId(), + } +} + +func (i *relation) Subject() *dsc3.ObjectIdentifier { + return &dsc3.ObjectIdentifier{ + ObjectType: i.GetSubjectType(), + ObjectId: i.GetSubjectId(), + } +} + func (i *relation) ObjKey() string { - return i.Object.GetType() + TypeIDSeparator + i.Object.GetKey() + + return i.GetObjectType() + TypeIDSeparator + i.GetObjectId() + InstanceSeparator + i.GetRelation() + InstanceSeparator + - i.Subject.GetType() + TypeIDSeparator + i.Subject.GetKey() + i.GetSubjectType() + TypeIDSeparator + i.GetSubjectId() + + RelationSeparator + + i.GetSubjectRelation() } +// TODO verify correct position of subject_relation in the key string func (i *relation) SubKey() string { - return i.Subject.GetType() + TypeIDSeparator + i.Subject.GetKey() + + return i.GetSubjectType() + TypeIDSeparator + i.GetSubjectId() + InstanceSeparator + i.GetRelation() + InstanceSeparator + - i.Object.GetType() + TypeIDSeparator + i.Object.GetKey() + i.GetObjectType() + TypeIDSeparator + i.GetObjectId() + + RelationSeparator + + i.GetSubjectRelation() } func (i *relation) Validate(mc *cache.Cache) (bool, error) { @@ -58,11 +99,11 @@ func (i *relation) Validate(mc *cache.Cache) (bool, error) { return false, ErrInvalidArgumentRelation.Msg("relation") } - if ok, err := ObjectIdentifier(i.Relation.Object).Validate(); !ok { + if ok, err := ObjectIdentifier(i.Object()).Validate(); !ok { return ok, err } - if ok, err := ObjectIdentifier(i.Relation.Subject).Validate(); !ok { + if ok, err := ObjectIdentifier(i.Subject()).Validate(); !ok { return ok, err } @@ -70,74 +111,16 @@ func (i *relation) Validate(mc *cache.Cache) (bool, error) { return true, nil } - if !mc.ObjectExists(model.ObjectName(*i.Relation.Object.Type)) { - return false, derr.ErrObjectTypeNotFound.Msg(*i.Relation.Object.Type) + if !mc.ObjectExists(model.ObjectName(i.GetObjectType())) { + return false, derr.ErrObjectTypeNotFound.Msg(i.GetObjectType()) } - if !mc.ObjectExists(model.ObjectName(*i.Relation.Subject.Type)) { - return false, derr.ErrObjectTypeNotFound.Msg(*i.Relation.Subject.Type) + if !mc.ObjectExists(model.ObjectName(i.GetSubjectType())) { + return false, derr.ErrObjectTypeNotFound.Msg(i.GetSubjectType()) } - if !mc.RelationExists(model.ObjectName(*i.Relation.Object.Type), model.RelationName(i.Relation.Relation)) { - return false, derr.ErrRelationTypeNotFound.Msg(*i.Relation.Object.Type + ":" + i.Relation.Relation) - } - - return true, nil -} - -// RelationIdentifier. -type relationIdentifier struct { - *dsc2.RelationIdentifier -} - -func RelationIdentifier(i *dsc2.RelationIdentifier) *relationIdentifier { - return &relationIdentifier{i} -} - -func (i *relationIdentifier) Key() string { - return i.ObjKey() -} - -func (i *relationIdentifier) ObjKey() string { - return i.Object.GetType() + TypeIDSeparator + i.Object.GetKey() + - InstanceSeparator + - i.Relation.GetName() + - InstanceSeparator + - i.Subject.GetType() + TypeIDSeparator + i.Subject.GetKey() -} - -func (i *relationIdentifier) SubKey() string { - return i.Subject.GetType() + TypeIDSeparator + i.Subject.GetKey() + - InstanceSeparator + - i.Relation.GetName() + - InstanceSeparator + - i.Object.GetType() + TypeIDSeparator + i.Object.GetKey() -} - -func (i *relationIdentifier) Validate() (bool, error) { - - if i == nil { - return false, derr.ErrInvalidRelationIdentifier.Msg("nil") - } - - if i.RelationIdentifier == nil { - return false, derr.ErrInvalidArgument.Msg("relation_identifier") - } - - if ok, err := ObjectSelector(i.RelationIdentifier.Object).Validate(); !ok { - return ok, err - } - - if i.RelationIdentifier.Relation != nil && (i.RelationIdentifier.Relation.ObjectType == nil || i.RelationIdentifier.Relation.GetObjectType() == "") { - i.Relation.ObjectType = i.Object.Type - } - - if ok, err := RelationTypeIdentifier(i.RelationIdentifier.Relation).Validate(); !ok { - return ok, err - } - - if ok, err := ObjectSelector(i.RelationIdentifier.Subject).Validate(); !ok { - return ok, err + if !mc.RelationExists(model.ObjectName(i.GetObjectType()), model.RelationName(i.GetRelation())) { + return false, derr.ErrRelationTypeNotFound.Msg(i.GetObjectType() + ":" + i.GetRelation()) } return true, nil @@ -148,35 +131,92 @@ func (i *relation) Hash() string { h.Reset() if i != nil && i.Relation != nil { - if i.Relation.Subject != nil { - if _, err := h.Write([]byte(i.Relation.Subject.GetKey())); err != nil { - return DefaultHash - } - if _, err := h.Write([]byte(i.Relation.Subject.GetType())); err != nil { - return DefaultHash - } + if _, err := h.Write([]byte(i.GetObjectId())); err != nil { + return DefaultHash } - if _, err := h.Write([]byte(i.Relation.Relation)); err != nil { + if _, err := h.Write([]byte(i.GetObjectType())); err != nil { return DefaultHash } - if i.Relation.Object != nil { - if _, err := h.Write([]byte(i.Relation.Object.GetKey())); err != nil { - return DefaultHash - } - if _, err := h.Write([]byte(i.Relation.Object.GetType())); err != nil { - return DefaultHash - } + if _, err := h.Write([]byte(i.GetRelation())); err != nil { + return DefaultHash + } + if _, err := h.Write([]byte(i.GetSubjectId())); err != nil { + return DefaultHash + } + if _, err := h.Write([]byte(i.GetSubjectType())); err != nil { + return DefaultHash + } + if _, err := h.Write([]byte(i.GetSubjectRelation())); err != nil { + return DefaultHash } } return strconv.FormatUint(h.Sum64(), 10) } -func (i *relationIdentifier) PathAndFilter() ([]string, string, error) { +// RelationIdentifier. +// type relationIdentifier struct { +// *dsc3.RelationIdentifier +// } + +// func RelationIdentifier(i *dsc3.RelationIdentifier) *relationIdentifier { +// return &relationIdentifier{i} +// } + +// func (i *relationIdentifier) Key() string { +// return i.ObjKey() +// } + +// func (i *relationIdentifier) ObjKey() string { +// return i.Object.GetType() + TypeIDSeparator + i.Object.GetKey() + +// InstanceSeparator + +// i.Relation.GetName() + +// InstanceSeparator + +// i.Subject.GetType() + TypeIDSeparator + i.Subject.GetKey() +// } + +// func (i *relationIdentifier) SubKey() string { +// return i.Subject.GetType() + TypeIDSeparator + i.Subject.GetKey() + +// InstanceSeparator + +// i.Relation.GetName() + +// InstanceSeparator + +// i.Object.GetType() + TypeIDSeparator + i.Object.GetKey() +// } + +// func (i *relationIdentifier) Validate() (bool, error) { + +// if i == nil { +// return false, derr.ErrInvalidRelationIdentifier.Msg("nil") +// } + +// if i.RelationIdentifier == nil { +// return false, derr.ErrInvalidArgument.Msg("relation_identifier") +// } + +// if ok, err := ObjectSelector(i.RelationIdentifier.Object).Validate(); !ok { +// return ok, err +// } + +// if i.RelationIdentifier.Relation != nil && (i.RelationIdentifier.Relation.ObjectType == nil || i.RelationIdentifier.Relation.GetObjectType() == "") { +// i.Relation.ObjectType = i.Object.Type +// } + +// if ok, err := RelationTypeIdentifier(i.RelationIdentifier.Relation).Validate(); !ok { +// return ok, err +// } + +// if ok, err := ObjectSelector(i.RelationIdentifier.Subject).Validate(); !ok { +// return ok, err +// } + +// return true, nil +// } + +func (i *relation) PathAndFilter() ([]string, string, error) { switch { - case ObjectSelector(i.RelationIdentifier.Object).IsComplete(): + case ObjectSelector(i.Object()).IsComplete(): return bdb.RelationsObjPath, i.ObjFilter(), nil - case ObjectSelector(i.RelationIdentifier.Subject).IsComplete(): + case ObjectSelector(i.Subject()).IsComplete(): return bdb.RelationsSubPath, i.SubFilter(), nil default: return []string{}, "", ErrNoCompleteObjectIdentifier @@ -186,33 +226,33 @@ func (i *relationIdentifier) PathAndFilter() ([]string, string, error) { // ObjFilter // format: obj_type : obj_id # relation @ sub_type : sub_id (# sub_relation). // TODO: if subject relation exists add subject relation to filter clause. -func (i *relationIdentifier) ObjFilter() string { +func (i *relation) ObjFilter() string { filter := strings.Builder{} - filter.WriteString(i.GetObject().GetType()) - filter.WriteByte(':') - filter.WriteString(i.GetObject().GetKey()) - filter.WriteByte('|') + filter.WriteString(i.GetObjectType()) + filter.WriteString(TypeIDSeparator) + filter.WriteString(i.GetObjectId()) + filter.WriteString(InstanceSeparator) - if IsNotSet(i.GetRelation().GetName()) { + if IsNotSet(i.GetRelation()) { return filter.String() } - filter.WriteString(i.GetRelation().GetName()) - filter.WriteByte('|') + filter.WriteString(i.GetRelation()) + filter.WriteString(InstanceSeparator) - if IsNotSet(i.GetSubject().GetType()) { + if IsNotSet(i.GetSubjectType()) { return filter.String() } - filter.WriteString(i.GetSubject().GetType()) - filter.WriteByte(':') + filter.WriteString(i.GetSubjectType()) + filter.WriteString(TypeIDSeparator) - if IsNotSet(i.GetSubject().GetKey()) { + if IsNotSet(i.GetSubjectId()) { return filter.String() } - filter.WriteString(i.GetSubject().GetKey()) + filter.WriteString(i.GetSubjectId()) return filter.String() } @@ -220,100 +260,40 @@ func (i *relationIdentifier) ObjFilter() string { // SubFilter // format: sub_type : sub_id (# sub_relation) | obj_type : obj_id # relation. // TODO: if subject relation exists add subject relation to filter clause. -func (i *relationIdentifier) SubFilter() string { +func (i *relation) SubFilter() string { filter := strings.Builder{} - filter.WriteString(i.GetSubject().GetType()) - filter.WriteByte(':') - filter.WriteString(i.GetSubject().GetKey()) - filter.WriteByte('|') + filter.WriteString(i.GetSubjectType()) + filter.WriteString(TypeIDSeparator) + filter.WriteString(i.GetSubjectId()) + filter.WriteString(InstanceSeparator) - if IsNotSet(i.GetRelation().GetName()) { + if IsNotSet(i.GetRelation()) { return filter.String() } - filter.WriteString(i.GetRelation().GetName()) - filter.WriteByte('|') + filter.WriteString(i.GetRelation()) + filter.WriteString(InstanceSeparator) - if IsNotSet(i.GetObject().GetType()) { + if IsNotSet(i.GetObjectType()) { return filter.String() } - filter.WriteString(i.GetObject().GetType()) - filter.WriteByte(':') + filter.WriteString(i.GetObjectType()) + filter.WriteString(TypeIDSeparator) - if IsNotSet(i.GetObject().GetKey()) { + if IsNotSet(i.GetObjectId()) { return filter.String() } - filter.WriteString(i.GetObject().GetKey()) + filter.WriteString(i.GetObjectId()) return filter.String() } -// RelationSelector. -type relationSelector struct { - *dsc2.RelationIdentifier -} - -func RelationSelector(i *dsc2.RelationIdentifier) *relationSelector { return &relationSelector{i} } - -func (i *relationSelector) Validate() (bool, error) { - if i == nil { - return false, derr.ErrInvalidRelationIdentifier.Msg("nil") - } - - if i.RelationIdentifier == nil { - i.RelationIdentifier = &dsc2.RelationIdentifier{ - Subject: &dsc2.ObjectIdentifier{}, - Relation: &dsc2.RelationTypeIdentifier{}, - Object: &dsc2.ObjectIdentifier{}, - } - } - - if i.RelationIdentifier.Subject == nil { - i.RelationIdentifier.Subject = &dsc2.ObjectIdentifier{} - } - - if i.RelationIdentifier.Relation == nil { - i.RelationIdentifier.Relation = &dsc2.RelationTypeIdentifier{} - } - - if i.RelationIdentifier.Object == nil { - i.RelationIdentifier.Object = &dsc2.ObjectIdentifier{} - } - - if ok, err := ObjectSelector(i.RelationIdentifier.Object).Validate(); !ok { - return ok, err - } - - if ok, err := RelationTypeSelector(i.RelationIdentifier.Relation).Validate(); !ok { - return ok, err - } - - if ok, err := ObjectSelector(i.RelationIdentifier.Subject).Validate(); !ok { - return ok, err - } - - // propagate object type to relation if missing. - if i.RelationIdentifier.Relation.GetObjectType() == "" { - i.RelationIdentifier.Relation.ObjectType = i.RelationIdentifier.Object.Type - } - - // relation:object_type and object:object_type must match - if i.RelationIdentifier.Relation.GetObjectType() != i.RelationIdentifier.Object.GetType() { - return false, errors.Wrapf(derr.ErrInvalidObjectType, "conflicting object types relation:%s object:%s", - i.RelationIdentifier.Relation.GetObjectType(), - i.RelationIdentifier.Object.GetType(), - ) - } - - return true, nil -} - -type RelationFilter func(*dsc2.Relation) bool +type RelationFilter func(*dsc3.Relation) bool -func (i *relationSelector) Filter() (bdb.Path, string, RelationFilter) { +func (i *relation) Filter() (bdb.Path, string, RelationFilter) { var ( path bdb.Path keyFilter string @@ -323,13 +303,13 @@ func (i *relationSelector) Filter() (bdb.Path, string, RelationFilter) { // set index path accordingly // set keyFilter to match covering path // when no complete object identifier, fallback to a full table scan - if ObjectIdentifier(i.Object).IsComplete() { + if ObjectIdentifier(i.Object()).IsComplete() { path = bdb.RelationsObjPath - keyFilter = RelationIdentifier(i.RelationIdentifier).ObjFilter() + keyFilter = i.ObjFilter() } - if ObjectIdentifier(i.Subject).IsComplete() { + if ObjectIdentifier(i.Subject()).IsComplete() { path = bdb.RelationsSubPath - keyFilter = RelationIdentifier(i.RelationIdentifier).SubFilter() + keyFilter = i.SubFilter() } if len(path) == 0 { log.Warn().Msg("!!! no covering index path, full scan !!!") @@ -338,52 +318,57 @@ func (i *relationSelector) Filter() (bdb.Path, string, RelationFilter) { } // #2 build valueFilter function - filters := []func(item *dsc2.Relation) bool{} + filters := []func(item *dsc3.Relation) bool{} - if i.RelationIdentifier.Object.GetType() != "" { - fv := i.RelationIdentifier.Object.GetType() - filters = append(filters, func(item *dsc2.Relation) bool { - equal := strings.Compare(item.Object.GetType(), fv) - log.Trace().Str("fv", fv).Str("item", item.Object.GetType()).Bool("equal", equal == 0).Msg("object_type filter") + if fv := i.GetObjectType(); fv != "" { + filters = append(filters, func(item *dsc3.Relation) bool { + equal := strings.Compare(item.GetObjectType(), fv) + log.Trace().Str("fv", fv).Str("item", item.GetObjectType()).Bool("equal", equal == 0).Msg("object_type filter") return equal == 0 }) } - if i.RelationIdentifier.Object.GetKey() != "" { - fv := i.RelationIdentifier.Object.GetKey() - filters = append(filters, func(item *dsc2.Relation) bool { - equal := strings.Compare(fv, item.Object.GetKey()) - log.Trace().Str("fv", fv).Str("item", item.Object.GetKey()).Bool("equal", equal == 0).Msg("object_id filter") + + if fv := i.GetObjectId(); fv != "" { + filters = append(filters, func(item *dsc3.Relation) bool { + equal := strings.Compare(fv, item.GetObjectId()) + log.Trace().Str("fv", fv).Str("item", item.GetObjectId()).Bool("equal", equal == 0).Msg("object_id filter") return equal == 0 }) } - if i.RelationIdentifier.Relation.GetName() != "" { - fv := i.RelationIdentifier.Relation.GetName() - filters = append(filters, func(item *dsc2.Relation) bool { + if fv := i.GetRelation(); fv != "" { + filters = append(filters, func(item *dsc3.Relation) bool { equal := strings.Compare(item.Relation, fv) log.Trace().Str("fv", fv).Str("item", item.Relation).Bool("equal", equal == 0).Msg("relation filter") return equal == 0 }) } - if i.RelationIdentifier.Subject.GetType() != "" { - fv := i.RelationIdentifier.Subject.GetType() - filters = append(filters, func(item *dsc2.Relation) bool { - equal := strings.Compare(item.Subject.GetType(), fv) - log.Trace().Str("fv", fv).Str("item", item.Subject.GetType()).Bool("equal", equal == 0).Msg("subject_type filter") + if fv := i.GetSubjectType(); fv != "" { + filters = append(filters, func(item *dsc3.Relation) bool { + equal := strings.Compare(item.GetSubjectType(), fv) + log.Trace().Str("fv", fv).Str("item", item.GetSubjectType()).Bool("equal", equal == 0).Msg("subject_type filter") + return equal == 0 + }) + } + + if fv := i.GetSubjectId(); fv != "" { + filters = append(filters, func(item *dsc3.Relation) bool { + equal := strings.Compare(fv, item.GetSubjectId()) + log.Trace().Str("fv", fv).Str("item", item.GetSubjectId()).Bool("equal", equal == 0).Msg("subject_id filter") return equal == 0 }) } - if i.RelationIdentifier.Subject.GetKey() != "" { - fv := i.RelationIdentifier.Subject.GetKey() - filters = append(filters, func(item *dsc2.Relation) bool { - equal := strings.Compare(fv, item.Subject.GetKey()) - log.Trace().Str("fv", fv).Str("item", item.Subject.GetKey()).Bool("equal", equal == 0).Msg("subject_id filter") + + if fv := i.GetSubjectRelation(); fv != "" { + filters = append(filters, func(item *dsc3.Relation) bool { + equal := strings.Compare(item.SubjectRelation, fv) + log.Trace().Str("fv", fv).Str("item", item.SubjectRelation).Bool("equal", equal == 0).Msg("subject_relation filter") return equal == 0 }) } - valueFilter := func(i *dsc2.Relation) bool { + valueFilter := func(i *dsc3.Relation) bool { for _, filter := range filters { if !filter(i) { return false diff --git a/pkg/ds/userset.go b/pkg/ds/userset.go index 99bff61..7d9f270 100644 --- a/pkg/ds/userset.go +++ b/pkg/ds/userset.go @@ -3,25 +3,25 @@ package ds import ( "context" - dsc2 "github.com/aserto-dev/go-directory/aserto/directory/common/v2" + dsc3 "github.com/aserto-dev/go-directory/aserto/directory/common/v3" "github.com/aserto-dev/go-edge-ds/pkg/bdb" bolt "go.etcd.io/bbolt" ) // CreateUserSet, create the computed user set of a subject. -func CreateUserSet(ctx context.Context, tx *bolt.Tx, subject *dsc2.ObjectIdentifier) ([]*dsc2.ObjectIdentifier, error) { - result := []*dsc2.ObjectIdentifier{subject} +func CreateUserSet(ctx context.Context, tx *bolt.Tx, subject *dsc3.ObjectIdentifier) ([]*dsc3.ObjectIdentifier, error) { + result := []*dsc3.ObjectIdentifier{subject} filter := ObjectIdentifier(subject).Key() + InstanceSeparator + "member" - relations, err := bdb.Scan[dsc2.Relation](ctx, tx, bdb.RelationsSubPath, filter) + relations, err := bdb.Scan[dsc3.Relation](ctx, tx, bdb.RelationsSubPath, filter) if err != nil { return nil, err } for _, r := range relations { - if r.Object.GetType() == "group" { - result = append(result, r.Object) + if r.GetObjectType() == "group" { + result = append(result, Relation(r).Object()) } }