From 64f259100d1b3e93ca80c99ef41175f4437a3fbc Mon Sep 17 00:00:00 2001 From: Ronen Hilewicz Date: Thu, 21 Mar 2024 18:42:55 -0400 Subject: [PATCH] Use special characters as separators --- graph/inverse_test.go | 14 ++++++-------- graph/objects.go | 12 ++++++------ graph/objects_test.go | 3 +-- graph/relation.go | 19 ------------------- model/inverse.go | 32 +++++++++++++++----------------- 5 files changed, 28 insertions(+), 52 deletions(-) diff --git a/graph/inverse_test.go b/graph/inverse_test.go index 9a03df4..fa7983c 100644 --- a/graph/inverse_test.go +++ b/graph/inverse_test.go @@ -23,14 +23,12 @@ func TestInversion(t *testing.T) { im := m.Invert() require.NotNil(im) - mnfst, err := manifest(im) - require.NoError(err) + mnfst := manifest(im) b, err := yaml.Marshal(mnfst) require.NoError(err) t.Logf("inverted model:\n%s\n", b) - // require.Fail("inverted model") } func TestReverseSearch(t *testing.T) { @@ -64,14 +62,14 @@ func TestReverseSearch(t *testing.T) { t.Run(test.search, func(tt *testing.T) { assert := req.New(tt) - req := invertedGraphReq(test.search) - s, err := graph.NewSubjectSearch(rm, req, reverseLookup(rm, rels.GetRelations)) + request := invertedGraphReq(test.search) + s, err := graph.NewSubjectSearch(rm, request, reverseLookup(rm, rels.GetRelations)) assert.NoError(err) res, err := s.Search() assert.NoError(err) - tt.Logf("request: +%v\n", req) + tt.Logf("request: +%v\n", request) tt.Logf("explanation: +%v\n", res.Explanation) tt.Logf("trace: +%v\n", res.Trace) @@ -92,7 +90,7 @@ func TestReverseSearch(t *testing.T) { } -func manifest(m *model.Model) (*v3.Manifest, error) { +func manifest(m *model.Model) *v3.Manifest { mnfst := v3.Manifest{ ModelInfo: &v3.ModelInfo{Version: v3.SchemaVersion(v3.SupportedSchemaVersion)}, ObjectTypes: lo.MapEntries(m.Objects, func(on model.ObjectName, o *model.Object) (v3.ObjectTypeName, *v3.ObjectType) { @@ -131,7 +129,7 @@ func manifest(m *model.Model) (*v3.Manifest, error) { }), } - return &mnfst, nil + return &mnfst } var reverseSearchTests = []searchTest{ diff --git a/graph/objects.go b/graph/objects.go index 05ac8b5..59c6f6e 100644 --- a/graph/objects.go +++ b/graph/objects.go @@ -1,7 +1,6 @@ package graph import ( - "fmt" "strings" "github.com/aserto-dev/azm/model" @@ -82,16 +81,17 @@ func invertGetGraphRequest(im *model.Model, req *dsr.GetGraphRequest) *relation iReq := &relation{ ot: model.ObjectName(req.SubjectType), oid: ObjectID(req.SubjectId), - rel: model.RelationName(fmt.Sprintf("%s_%s", req.ObjectType, req.Relation)), + rel: model.InverseRelation(model.ObjectName(req.ObjectType), model.RelationName(req.Relation)), st: model.ObjectName(req.ObjectType), sid: ObjectID(req.ObjectId), // TODO: what do we do with subject relations // srel: model.RelationName(req.SubjectRelation), } - o := im.Objects[model.ObjectName(iReq.ot)] - if o.HasRelation(model.RelationName(iReq.rel)) && o.HasPermission(model.RelationName("r_"+iReq.rel)) { - iReq.rel = model.RelationName("r_" + iReq.rel) + o := im.Objects[iReq.ot] + srPerm := model.SubjectRelationPrefix + iReq.rel + if o.HasRelation(iReq.rel) && o.HasPermission(srPerm) { + iReq.rel = srPerm } return iReq @@ -105,7 +105,7 @@ func wildcardParams(params *relation) *relation { func invertedRelationReader(reader RelationReader) RelationReader { return func(r *dsc.Relation) ([]*dsc.Relation, error) { - x := strings.SplitN(r.Relation, "_", 2) + x := strings.SplitN(r.Relation, model.ObjectNameSeparator, 2) rr := &dsc.Relation{ ObjectType: r.SubjectType, diff --git a/graph/objects_test.go b/graph/objects_test.go index f2d5458..3b31ba9 100644 --- a/graph/objects_test.go +++ b/graph/objects_test.go @@ -22,8 +22,7 @@ func TestSearchObjects(t *testing.T) { require.NoError(err) require.NotNil(m) - mnfst, err := manifest(m.Invert()) - require.NoError(err) + mnfst := manifest(m.Invert()) b, err := yaml.Marshal(mnfst) require.NoError(err) diff --git a/graph/relation.go b/graph/relation.go index e47ac0d..7c051e3 100644 --- a/graph/relation.go +++ b/graph/relation.go @@ -31,18 +31,6 @@ func relationFromProto(rel *dsc.Relation) *relation { } } -// converts a relation to a dsc.Relation. -func (p *relation) toProto() *dsc.Relation { - return &dsc.Relation{ - ObjectType: p.ot.String(), - ObjectId: p.oid.String(), - Relation: p.rel.String(), - SubjectType: p.st.String(), - SubjectId: p.sid.String(), - SubjectRelation: p.srel.String(), - } -} - func (p *relation) String() string { str := fmt.Sprintf("%s:%s#%s@%s:%s", p.ot, displayID(p.oid), p.rel, p.st, displayID(p.sid)) if p.srel != "" { @@ -51,13 +39,6 @@ func (p *relation) String() string { return str } -func (p *relation) object() *object { - return &object{ - Type: p.ot, - ID: p.oid, - } -} - func (p *relation) subject() *object { return &object{ Type: p.st, diff --git a/model/inverse.go b/model/inverse.go index 0352b8a..937808f 100644 --- a/model/inverse.go +++ b/model/inverse.go @@ -6,6 +6,11 @@ import ( "github.com/samber/lo" ) +const ( + ObjectNameSeparator = "^" + SubjectRelationPrefix = "$" +) + func (m *Model) Invert() *Model { return newInverter(m).invert() } @@ -29,6 +34,9 @@ func newInverter(m *Model) *inverter { } func (i *inverter) invert() *Model { + // invert all relations before inverting permissions. + // this is necessary to create synthetic permissions for subject relations. + // these permissions are stored in the substitution map (i.subst) and used in inverted permissions. for on, o := range i.m.Objects { for rn, r := range o.Relations { i.invertRelation(on, rn, r) @@ -38,7 +46,7 @@ func (i *inverter) invert() *Model { for on, o := range i.m.Objects { for pn, p := range o.Permissions { kind := kindOf(p) - ipn := irel(on, pn) + ipn := InverseRelation(on, pn) for _, pt := range p.Terms() { switch { @@ -62,16 +70,6 @@ func (i *inverter) invert() *Model { for _, rr := range r.Union { ip := permissionOrNew(i.im.Objects[rr.Object], ipn, kind) ip.AddTerm(&PermissionTerm{RelOrPerm: i.irelSub(on, pt.RelOrPerm)}) - - // if rr.IsSubject() { - // term := &PermissionTerm{Base: i.irelSub(rr.Object, rr.Relation), RelOrPerm: ipn} - // ip.AddTerm(term) - - // for _, subj := range subjs(i.m.Objects[rr.Object], rr.Relation) { - // ip = permissionOrNew(i.im.Objects[subj], ipn, kind) - // ip.AddTerm(term) - // } - // } } case o.HasPermission(pt.RelOrPerm): @@ -89,7 +87,7 @@ func (i *inverter) invert() *Model { func (i *inverter) invertRelation(on ObjectName, rn RelationName, r *Relation) { for _, rr := range r.Union { - irn := irel(on, rn) + irn := InverseRelation(on, rn) i.im.Objects[rr.Object].Relations[irn] = &Relation{Union: []*RelationRef{{Object: on}}} if rr.IsSubject() { // add a synthetic permission to reverse the expansion of the subject relation @@ -99,7 +97,7 @@ func (i *inverter) invertRelation(on ObjectName, rn RelationName, r *Relation) { for _, subj := range subjects { p := permissionOrNew(i.im.Objects[subj], ipn, permissionKindUnion) p.AddTerm(&PermissionTerm{RelOrPerm: irn}) - rel := irel(rr.Object, rr.Relation) + rel := InverseRelation(rr.Object, rr.Relation) base := lo.Ternary(rr.Object == on, rel, i.sub(rel)) p.AddTerm(&PermissionTerm{Base: base, RelOrPerm: ipn}) i.addSubstitution(irn, ipn) @@ -109,7 +107,7 @@ func (i *inverter) invertRelation(on ObjectName, rn RelationName, r *Relation) { } func (i *inverter) irelSub(on ObjectName, rn RelationName) RelationName { - return i.sub(irel(on, rn)) + return i.sub(InverseRelation(on, rn)) } func (i *inverter) sub(rn RelationName) RelationName { @@ -179,12 +177,12 @@ func permissionOrNew(o *Object, pn RelationName, kind permissionKind) *Permissio return p } -func irel(on ObjectName, rn RelationName) RelationName { - return RelationName(fmt.Sprintf("%s_%s", on, rn)) +func InverseRelation(on ObjectName, rn RelationName) RelationName { + return RelationName(fmt.Sprintf("%s%s%s", on, ObjectNameSeparator, rn)) } func rsrel(on ObjectName, rn RelationName) RelationName { - return RelationName(fmt.Sprintf("r_%s", irel(on, rn))) + return RelationName(fmt.Sprintf("%s%s", SubjectRelationPrefix, InverseRelation(on, rn))) } func subjs(o *Object, rn RelationName) []ObjectName {