@@ -36,6 +36,20 @@ func (r *RelationIterator) buildSubjectRelationFilter() datastore.SubjectRelatio
3636}
3737
3838func (r * RelationIterator ) CheckImpl (ctx * Context , resources []Object , subject ObjectAndRelation ) (RelationSeq , error ) {
39+ // If the subject type doesn't match the base relation type, return no results
40+ if subject .ObjectType != r .base .Type {
41+ return func (yield func (Relation , error ) bool ) {
42+ // Empty sequence
43+ }, nil
44+ }
45+
46+ if r .base .Wildcard {
47+ return r .checkWildcardImpl (ctx , resources , subject )
48+ }
49+ return r .checkNormalImpl (ctx , resources , subject )
50+ }
51+
52+ func (r * RelationIterator ) checkNormalImpl (ctx * Context , resources []Object , subject ObjectAndRelation ) (RelationSeq , error ) {
3953 resourceIDs := make ([]string , len (resources ))
4054 for i , res := range resources {
4155 resourceIDs [i ] = res .ObjectID
@@ -68,14 +82,101 @@ func (r *RelationIterator) CheckImpl(ctx *Context, resources []Object, subject O
6882 return RelationSeq (relIter ), nil
6983}
7084
85+ func (r * RelationIterator ) checkWildcardImpl (ctx * Context , resources []Object , subject ObjectAndRelation ) (RelationSeq , error ) {
86+ // Query the datastore for wildcard relationships (subject ObjectID = "*")
87+ resourceIDs := make ([]string , len (resources ))
88+ for i , res := range resources {
89+ resourceIDs [i ] = res .ObjectID
90+ }
91+
92+ filter := datastore.RelationshipsFilter {
93+ OptionalResourceType : r .base .DefinitionName (),
94+ OptionalResourceIds : resourceIDs ,
95+ OptionalResourceRelation : r .base .RelationName (),
96+ OptionalSubjectsSelectors : []datastore.SubjectsSelector {
97+ {
98+ OptionalSubjectType : r .base .Type ,
99+ OptionalSubjectIds : []string {tuple .PublicWildcard }, // Look for "*" subjects
100+ RelationFilter : r .buildSubjectRelationFilter (),
101+ },
102+ },
103+ }
104+
105+ reader := ctx .Datastore .SnapshotReader (ctx .Revision )
106+
107+ relIter , err := reader .QueryRelationships (ctx , filter ,
108+ options .WithSkipCaveats (r .base .Caveat == "" ),
109+ options .WithSkipExpiration (! r .base .Expiration ),
110+ options .WithQueryShape (queryshape .CheckPermissionSelectDirectSubjects ),
111+ )
112+ if err != nil {
113+ return nil , err
114+ }
115+
116+ // Transform the wildcard relationships to use the concrete subject
117+ return func (yield func (Relation , error ) bool ) {
118+ for rel , err := range relIter {
119+ if err != nil {
120+ if ! yield (rel , err ) {
121+ return
122+ }
123+ continue
124+ }
125+
126+ // Replace the wildcard subject with the concrete subject
127+ concreteRel := rel
128+ concreteRel .Subject = subject
129+
130+ if ! yield (concreteRel , nil ) {
131+ return
132+ }
133+ }
134+ }, nil
135+ }
136+
71137func (r * RelationIterator ) IterSubjectsImpl (ctx * Context , resource Object ) (RelationSeq , error ) {
138+ if r .base .Wildcard {
139+ return r .iterSubjectsWildcardImpl (ctx , resource )
140+ }
141+ return r .iterSubjectsNormalImpl (ctx , resource )
142+ }
143+
144+ func (r * RelationIterator ) iterSubjectsNormalImpl (ctx * Context , resource Object ) (RelationSeq , error ) {
145+ filter := datastore.RelationshipsFilter {
146+ OptionalResourceType : r .base .DefinitionName (),
147+ OptionalResourceIds : []string {resource .ObjectID },
148+ OptionalResourceRelation : r .base .RelationName (),
149+ OptionalSubjectsSelectors : []datastore.SubjectsSelector {
150+ {
151+ OptionalSubjectType : r .base .Type ,
152+ RelationFilter : r .buildSubjectRelationFilter (),
153+ },
154+ },
155+ }
156+
157+ reader := ctx .Datastore .SnapshotReader (ctx .Revision )
158+
159+ relIter , err := reader .QueryRelationships (ctx , filter ,
160+ options .WithSkipCaveats (r .base .Caveat == "" ),
161+ options .WithSkipExpiration (! r .base .Expiration ),
162+ options .WithQueryShape (queryshape .AllSubjectsForResources ),
163+ )
164+ if err != nil {
165+ return nil , err
166+ }
167+
168+ return RelationSeq (relIter ), nil
169+ }
170+
171+ func (r * RelationIterator ) iterSubjectsWildcardImpl (ctx * Context , resource Object ) (RelationSeq , error ) {
72172 filter := datastore.RelationshipsFilter {
73173 OptionalResourceType : r .base .DefinitionName (),
74174 OptionalResourceIds : []string {resource .ObjectID },
75175 OptionalResourceRelation : r .base .RelationName (),
76176 OptionalSubjectsSelectors : []datastore.SubjectsSelector {
77177 {
78178 OptionalSubjectType : r .base .Type ,
179+ OptionalSubjectIds : []string {tuple .PublicWildcard }, // Look for "*" subjects
79180 RelationFilter : r .buildSubjectRelationFilter (),
80181 },
81182 },
@@ -106,7 +207,13 @@ func (r *RelationIterator) Clone() Iterator {
106207}
107208
108209func (r * RelationIterator ) Explain () Explain {
210+ relationName := r .base .Subrelation
211+ if r .base .Wildcard {
212+ relationName = "*"
213+ }
109214 return Explain {
110- Info : fmt .Sprintf ("Relation(%s:%s, \" %s\" , caveat: %v, expiration: %v)" , r .base .DefinitionName (), r .base .RelationName (), r .base .Subrelation , r .base .Caveat != "" , r .base .Expiration ),
215+ Info : fmt .Sprintf ("Relation(%s:%s -> %s:%s, caveat: %v, expiration: %v)" ,
216+ r .base .DefinitionName (), r .base .RelationName (), r .base .Type , relationName ,
217+ r .base .Caveat != "" , r .base .Expiration ),
111218 }
112219}
0 commit comments