diff --git a/internal/developmentmembership/membership.go b/internal/developmentmembership/membership.go index d84becf4fc..5c4d28fa20 100644 --- a/internal/developmentmembership/membership.go +++ b/internal/developmentmembership/membership.go @@ -157,6 +157,8 @@ func populateFoundSubjects(rootONR *core.ObjectAndRelation, treeNode *core.Relat fs.relationships.Add(resource) } + + toReturn.ApplyParentCaveatExpression(treeNode.CaveatExpression) return toReturn, nil default: diff --git a/internal/dispatch/graph/expand_test.go b/internal/dispatch/graph/expand_test.go index e38b0fd56c..04b10aaa49 100644 --- a/internal/dispatch/graph/expand_test.go +++ b/internal/dispatch/graph/expand_test.go @@ -632,6 +632,95 @@ func TestCaveatedExpand(t *testing.T) { } `, }, + { + "recursive caveated indirect arrow", + `definition user {} + + caveat somecaveat(somecondition int) { + somecondition == 42 + } + + definition folder { + relation container: folder with somecaveat + relation member: user + permission view = container->member + } + + definition resource { + relation folder: folder + permission view = folder->view + }`, + []*core.RelationTuple{ + tuple.MustParse("resource:someresource#folder@folder:first"), + tuple.MustParse("folder:first#container@folder:second[somecaveat]"), + tuple.MustParse("folder:first#member@user:notreachable"), + tuple.MustParse("folder:second#member@user:tom"), + }, + tuple.ParseONR("resource:someresource#view"), + v1.DispatchExpandRequest_RECURSIVE, + ` + intermediate_node: { + operation: UNION + child_nodes: { + intermediate_node: { + operation: UNION + child_nodes: { + intermediate_node: { + operation: UNION + child_nodes: { + intermediate_node: { + operation: UNION + child_nodes: { + leaf_node: { + subjects: { + subject: { + namespace: "user" + object_id: "tom" + relation: "..." + } + } + } + expanded: { + namespace: "folder" + object_id: "second" + relation: "member" + } + caveat_expression: { + caveat: { + caveat_name: "somecaveat" + context: {} + } + } + } + } + expanded: { + namespace: "folder" + object_id: "first" + relation: "view" + } + } + } + expanded: { + namespace: "folder" + object_id: "first" + relation: "view" + } + } + } + expanded: { + namespace: "resource" + object_id: "someresource" + relation: "view" + } + } + } + expanded: { + namespace: "resource" + object_id: "someresource" + relation: "view" + } + `, + }, } for _, tc := range testCases { diff --git a/internal/services/integrationtesting/testconfigs/caveatindirectarrow.yaml b/internal/services/integrationtesting/testconfigs/caveatindirectarrow.yaml new file mode 100644 index 0000000000..616285a71b --- /dev/null +++ b/internal/services/integrationtesting/testconfigs/caveatindirectarrow.yaml @@ -0,0 +1,40 @@ +schema: |- + definition user {} + + caveat team_is_admin(admin_teams list, self string) { + self in admin_teams + } + + definition team { + relation admin: team with team_is_admin + relation member: user + permission view = admin->member + } + + definition resource { + relation team: team + permission can_view = team->view + } +relationships: |- + // team definition + team:A#admin@team:A[team_is_admin:{"self":"A"}] + team:B#admin@team:B[team_is_admin:{"self":"B"}] + team:C#admin@team:C[team_is_admin:{"self":"C"}] + // team members + team:A#member@user:clara + // resources + resource:1#team@team:B + resource:2#team@team:C + resource:3#team@team:C + resource:4#team@team:A + resource:5#team@team:B + resource:6#team@team:B + resource:7#team@team:C +assertions: + assertTrue: + - 'resource:4#can_view@user:clara with {"admin_teams": ["A"]}' + assertCaveated: + - resource:4#can_view@user:clara + assertFalse: + - 'resource:4#can_view@user:clara with {"admin_teams": ["B"]}' + - 'resource:1#can_view@user:clara with {"admin_teams": ["B"]}'