Skip to content

Commit a244ed1

Browse files
authored
Merge pull request from GHSA-j85q-46hg-36p2
Fix SubjectSetByType to properly *merge* mapped relations
2 parents 095ffd3 + 9d0a855 commit a244ed1

File tree

5 files changed

+137
-1
lines changed

5 files changed

+137
-1
lines changed

internal/datasets/subjectset.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ func (ss SubjectSet) MustUnionWithSet(other SubjectSet) {
3636
ss.BaseSubjectSet.MustUnionWithSet(other.BaseSubjectSet)
3737
}
3838

39+
func (ss SubjectSet) Clone() SubjectSet {
40+
return SubjectSet{ss.BaseSubjectSet.Clone()}
41+
}
42+
3943
func (ss SubjectSet) UnionWithSet(other SubjectSet) error {
4044
return ss.BaseSubjectSet.UnionWithSet(other.BaseSubjectSet)
4145
}

internal/datasets/subjectsetbytype.go

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,17 @@ func (s *SubjectByTypeSet) Map(mapper func(rr *core.RelationReference) (*core.Re
6969
if updatedType == nil {
7070
continue
7171
}
72-
mapped.byType[tuple.JoinRelRef(updatedType.Namespace, updatedType.Relation)] = subjectset
72+
73+
key := tuple.JoinRelRef(updatedType.Namespace, updatedType.Relation)
74+
if existing, ok := mapped.byType[key]; ok {
75+
cloned := subjectset.Clone()
76+
if err := cloned.UnionWithSet(existing); err != nil {
77+
return nil, err
78+
}
79+
mapped.byType[key] = cloned
80+
} else {
81+
mapped.byType[key] = subjectset
82+
}
7383
}
7484
return mapped, nil
7585
}

internal/datasets/subjectsetbytype_test.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66

77
"github.com/stretchr/testify/require"
88

9+
"github.com/authzed/spicedb/pkg/genutil/mapz"
910
core "github.com/authzed/spicedb/pkg/proto/core/v1"
1011
"github.com/authzed/spicedb/pkg/tuple"
1112
)
@@ -104,3 +105,29 @@ func TestSubjectSetByTypeWithCaveats(t *testing.T) {
104105
tom.GetCaveatExpression(),
105106
)
106107
}
108+
109+
func TestSubjectSetMapOverSameSubjectDifferentRelation(t *testing.T) {
110+
set := NewSubjectByTypeSet()
111+
require.True(t, set.IsEmpty())
112+
113+
err := set.AddSubjectOf(tuple.MustParse("document:foo#folder@folder:folder1"))
114+
require.NoError(t, err)
115+
116+
err = set.AddSubjectOf(tuple.MustParse("document:foo#folder@folder:folder2#parent"))
117+
require.NoError(t, err)
118+
119+
mapped, err := set.Map(func(rr *core.RelationReference) (*core.RelationReference, error) {
120+
return &core.RelationReference{
121+
Namespace: rr.Namespace,
122+
Relation: "shared",
123+
}, nil
124+
})
125+
require.NoError(t, err)
126+
127+
foundSubjectIDs := mapz.NewSet[string]()
128+
for _, sub := range mapped.byType["folder#shared"].AsSlice() {
129+
foundSubjectIDs.Add(sub.SubjectId)
130+
}
131+
132+
require.ElementsMatch(t, []string{"folder1", "folder2"}, foundSubjectIDs.AsSlice())
133+
}

internal/dispatch/graph/lookupsubjects_test.go

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -640,6 +640,73 @@ func TestCaveatedLookupSubjects(t *testing.T) {
640640
},
641641
},
642642
},
643+
{
644+
"arrow over different relations of the same subject",
645+
`definition user {}
646+
647+
definition folder {
648+
relation parent: folder
649+
relation viewer: user
650+
permission view = viewer
651+
}
652+
653+
definition document {
654+
relation folder: folder | folder#parent
655+
permission view = folder->view
656+
}`,
657+
[]*corev1.RelationTuple{
658+
tuple.MustParse("folder:folder1#viewer@user:tom"),
659+
tuple.MustParse("folder:folder2#viewer@user:fred"),
660+
tuple.MustParse("document:somedoc#folder@folder:folder1"),
661+
tuple.MustParse("document:somedoc#folder@folder:folder2#parent"),
662+
},
663+
ONR("document", "somedoc", "view"),
664+
RR("user", "..."),
665+
[]*v1.FoundSubject{
666+
{
667+
SubjectId: "tom",
668+
},
669+
{
670+
SubjectId: "fred",
671+
},
672+
},
673+
},
674+
{
675+
"caveated arrow over different relations of the same subject",
676+
`definition user {}
677+
678+
caveat somecaveat(somecondition int) {
679+
somecondition == 42
680+
}
681+
682+
definition folder {
683+
relation parent: folder
684+
relation viewer: user
685+
permission view = viewer
686+
}
687+
688+
definition document {
689+
relation folder: folder | folder#parent with somecaveat
690+
permission view = folder->view
691+
}`,
692+
[]*corev1.RelationTuple{
693+
tuple.MustParse("folder:folder1#viewer@user:tom"),
694+
tuple.MustParse("folder:folder2#viewer@user:fred"),
695+
tuple.MustParse("document:somedoc#folder@folder:folder1"),
696+
tuple.MustWithCaveat(tuple.MustParse("document:somedoc#folder@folder:folder2#parent"), "somecaveat"),
697+
},
698+
ONR("document", "somedoc", "view"),
699+
RR("user", "..."),
700+
[]*v1.FoundSubject{
701+
{
702+
SubjectId: "tom",
703+
},
704+
{
705+
SubjectId: "fred",
706+
CaveatExpression: caveatexpr("somecaveat"),
707+
},
708+
},
709+
},
643710
}
644711

645712
for _, tc := range testCases {
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
---
2+
schema: |+
3+
definition user {}
4+
5+
definition folder {
6+
relation parent: folder
7+
8+
relation viewer: user
9+
permission view = viewer
10+
}
11+
12+
definition document {
13+
relation folder: folder#parent | folder
14+
permission view = folder->view
15+
}
16+
17+
relationships: >-
18+
document:firstdoc#folder@folder:folder1
19+
20+
document:firstdoc#folder@folder:folder2#parent
21+
22+
folder:folder1#viewer@user:tom
23+
24+
folder:folder2#viewer@user:fred
25+
assertions:
26+
assertTrue:
27+
- "document:firstdoc#view@user:tom#..."
28+
- "document:firstdoc#view@user:fred#..."

0 commit comments

Comments
 (0)