Skip to content

Commit d909242

Browse files
authored
Merge pull request #2263 from authzed/OS-222/disallow-def-partial-same-name
OS-222: Require definitions and partials to have distinct names
2 parents c55d23e + 5fadd82 commit d909242

File tree

3 files changed

+60
-11
lines changed

3 files changed

+60
-11
lines changed

pkg/composableschemadsl/compiler/compiler.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,14 +130,16 @@ func Compile(schema InputSchema, prefix ObjectPrefixOption, opts ...Option) (*Co
130130
return nil, err
131131
}
132132

133+
initialCompiledPartials := make(map[string][]*core.Relation)
134+
133135
compiled, err := translate(&translationContext{
134136
objectTypePrefix: cfg.objectTypePrefix,
135137
mapper: mapper,
136138
schemaString: schema.SchemaString,
137139
skipValidate: cfg.skipValidation,
138140
allowedFlags: cfg.allowedFlags,
139141
existingNames: mapz.NewSet[string](),
140-
compiledPartials: make(map[string][]*core.Relation),
142+
compiledPartials: initialCompiledPartials,
141143
unresolvedPartials: mapz.NewMultiMap[string, *dslNode](),
142144
}, root)
143145
if err != nil {

pkg/composableschemadsl/compiler/compiler_test.go

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ func TestCompile(t *testing.T) {
5050
"nested parse error",
5151
withTenantPrefix,
5252
`definition foo {
53-
relation something: rela | relb + relc
53+
relation something: rela | relb + relc
5454
}`,
5555
"parse error in `nested parse error`, line 2, column 37: Expected end of statement or definition, found: TokenTypePlus",
5656
[]SchemaDefinition{},
@@ -307,6 +307,36 @@ func TestCompile(t *testing.T) {
307307
"could not find partial reference",
308308
[]SchemaDefinition{},
309309
},
310+
{
311+
"definition with same name as partial",
312+
withTenantPrefix,
313+
`
314+
partial simple {
315+
relation user: user
316+
}
317+
definition simple {
318+
...simple
319+
}
320+
`,
321+
"found definition with same name as existing partial",
322+
[]SchemaDefinition{},
323+
},
324+
{
325+
"caveat with same name as partial",
326+
withTenantPrefix,
327+
`
328+
caveat some_caveat(someparam int) { someparam == 42}
329+
330+
partial some_caveat {
331+
relation user: user
332+
}
333+
definition simple {
334+
...some_caveat
335+
}
336+
`,
337+
"found caveat with same name as existing partial",
338+
[]SchemaDefinition{},
339+
},
310340
{
311341
"definition reference to another definition errors",
312342
withTenantPrefix,
@@ -925,7 +955,7 @@ func TestCompile(t *testing.T) {
925955
"thirdParam": caveattypes.MustListType(caveattypes.IntType),
926956
},
927957
), "sometenant/foo",
928-
`someParam == 42 && someParam != 43 && someParam < 12 && someParam > 56
958+
`someParam == 42 && someParam != 43 && someParam < 12 && someParam > 56
929959
&& anotherParam == "hi there" && 42 in thirdParam`),
930960
},
931961
},
@@ -1210,7 +1240,7 @@ func TestCompile(t *testing.T) {
12101240
"relation with expiration trait",
12111241
withTenantPrefix,
12121242
`use expiration
1213-
1243+
12141244
definition simple {
12151245
relation viewer: user with expiration
12161246
}`,
@@ -1250,7 +1280,7 @@ func TestCompile(t *testing.T) {
12501280
"relation with expiration trait and caveat",
12511281
withTenantPrefix,
12521282
`use expiration
1253-
1283+
12541284
definition simple {
12551285
relation viewer: user with somecaveat and expiration
12561286
}`,

pkg/composableschemadsl/compiler/translator.go

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,10 @@ func translate(tctx *translationContext, root *dslNode) (*CompiledSchema, error)
9595
return nil, topLevelNode.WithSourceErrorf(name, "found name reused between multiple definitions and/or caveats: %s", name)
9696
}
9797

98+
if _, found := tctx.compiledPartials[name]; found {
99+
return nil, topLevelNode.WithSourceErrorf(name, "found caveat with same name as existing partial: %s", name)
100+
}
101+
98102
orderedDefinitions = append(orderedDefinitions, def)
99103

100104
case dslshape.NodeTypeDefinition:
@@ -111,6 +115,10 @@ func translate(tctx *translationContext, root *dslNode) (*CompiledSchema, error)
111115
return nil, topLevelNode.WithSourceErrorf(name, "found name reused between multiple definitions and/or caveats: %s", name)
112116
}
113117

118+
if _, found := tctx.compiledPartials[name]; found {
119+
return nil, topLevelNode.WithSourceErrorf(name, "found definition with same name as existing partial: %s", name)
120+
}
121+
114122
orderedDefinitions = append(orderedDefinitions, def)
115123
}
116124
}
@@ -864,6 +872,11 @@ func translatePartial(tctx *translationContext, partialNode *dslNode) error {
864872
if err != nil {
865873
return err
866874
}
875+
// Use the prefixed path to align with namespace and definition compilation
876+
partialPath, err := tctx.prefixedPath(partialName)
877+
if err != nil {
878+
return err
879+
}
867880
// This needs to return the unresolved name.
868881
errorOnMissingReference := false
869882
relationsAndPermissions, unresolvedPartial, err := translateRelationsAndPermissions(tctx, partialNode, errorOnMissingReference)
@@ -875,22 +888,22 @@ func translatePartial(tctx *translationContext, partialNode *dslNode) error {
875888
return nil
876889
}
877890

878-
tctx.compiledPartials[partialName] = relationsAndPermissions
891+
tctx.compiledPartials[partialPath] = relationsAndPermissions
879892

880893
// Since we've successfully compiled a partial, check the unresolved partials to see if any other partial was
881894
// waiting on this partial
882895
// NOTE: we're making an assumption here that a partial can't end up back in the same
883896
// list of unresolved partials - if it hangs again in a different spot, it will end up in a different
884897
// list of unresolved partials.
885-
waitingPartials, _ := tctx.unresolvedPartials.Get(partialName)
898+
waitingPartials, _ := tctx.unresolvedPartials.Get(partialPath)
886899
for _, waitingPartialNode := range waitingPartials {
887900
err := translatePartial(tctx, waitingPartialNode)
888901
if err != nil {
889902
return err
890903
}
891904
}
892905
// Clear out this partial's key from the unresolved partials if it's not already empty.
893-
tctx.unresolvedPartials.RemoveKey(partialName)
906+
tctx.unresolvedPartials.RemoveKey(partialPath)
894907
return nil
895908
}
896909

@@ -902,14 +915,18 @@ func translatePartialReference(tctx *translationContext, partialReferenceNode *d
902915
if err != nil {
903916
return nil, "", err
904917
}
905-
relationsAndPermissions, ok := tctx.compiledPartials[name]
918+
path, err := tctx.prefixedPath(name)
919+
if err != nil {
920+
return nil, "", err
921+
}
922+
relationsAndPermissions, ok := tctx.compiledPartials[path]
906923
if !ok {
907924
if errorOnMissingReference {
908-
return nil, "", partialReferenceNode.Errorf("could not find partial reference with name %s", name)
925+
return nil, "", partialReferenceNode.Errorf("could not find partial reference with name %s", path)
909926
}
910927
// If the partial isn't present and we're not throwing an error, we return the name of the missing partial
911928
// This behavior supports partial collection
912-
return nil, name, nil
929+
return nil, path, nil
913930
}
914931
return relationsAndPermissions, "", nil
915932
}

0 commit comments

Comments
 (0)