graph: Add support for "or" filter on /users

This add support for combining filters on the /users with a logical "or" operation.
E.g. the filter:

"filter=(memberOf/any(m:m/id eq 509a9dcd-bb37-4f4f-a01a-19dca27d9cfa) or memberOf/any(m:m/id eq 262982c1-2362-4afa-bfdf-8cbfef64a06e)"

will return all users that are a member of either of the referenced group.

Closes: #5667
This commit is contained in:
Ralf Haferkamp
2023-03-01 13:09:03 +01:00
committed by Ralf Haferkamp
parent e63e466188
commit db752dc5ac
2 changed files with 36 additions and 1 deletions

View File

@@ -50,6 +50,12 @@ func (g Graph) applyFilterLogical(ctx context.Context, req *godata.GoDataRequest
return users, invalidFilterError()
}
return g.applyFilterLogicalAnd(ctx, req, root.Children[0], root.Children[1])
case "or":
// 'or' needs 2 operands
if len(root.Children) != 2 {
return users, invalidFilterError()
}
return g.applyFilterLogicalOr(ctx, req, root.Children[0], root.Children[1])
}
logger.Debug().Str("Token", root.Token.Value).Msg("unsupported logical filter")
return users, unsupportedFilterError()
@@ -109,6 +115,32 @@ func (g Graph) applyFilterLogicalAnd(ctx context.Context, req *godata.GoDataRequ
return filteredUsers, nil
}
func (g Graph) applyFilterLogicalOr(ctx context.Context, req *godata.GoDataRequest, operand1 *godata.ParseNode, operand2 *godata.ParseNode) (users []*libregraph.User, err error) {
// logger := g.logger.SubloggerWithRequestID(ctx)
var res1, res2 []*libregraph.User
res1, err = g.applyUserFilter(ctx, req, operand1)
if err != nil {
return []*libregraph.User{}, err
}
res2, err = g.applyUserFilter(ctx, req, operand2)
if err != nil {
return []*libregraph.User{}, err
}
// We now have two slices with results of the subfilters. Now turn one of them
// into a map for efficiently getting the union of both slices
userSet := userSliceToMap(res1)
filteredUsers := make([]*libregraph.User, 0, len(res1)+len(res2))
filteredUsers = append(filteredUsers, res1...)
for _, user := range res2 {
if _, found := userSet[user.GetId()]; !found {
filteredUsers = append(filteredUsers, user)
}
}
return filteredUsers, nil
}
func (g Graph) applyFilterLambda(ctx context.Context, req *godata.GoDataRequest, nodes []*godata.ParseNode) (users []*libregraph.User, err error) {
logger := g.logger.SubloggerWithRequestID(ctx)
if len(nodes) != 2 {

View File

@@ -378,9 +378,12 @@ var _ = Describe("Users", func() {
Entry("with memberOf lambda filter with UUID", "memberOf/any(n:n/id eq 25cb7bc0-3168-4a0c-adbe-396f478ad494)", http.StatusOK),
Entry("with memberOf lambda filter with UUID string", "memberOf/any(n:n/id eq '25cb7bc0-3168-4a0c-adbe-396f478ad494')", http.StatusOK),
Entry("with appRoleAssignments lambda filter with appRoleId", "appRoleAssignments/any(n:n/appRoleId eq 'some-appRole-ID')", http.StatusOK),
Entry("with two memberOf lambda filters",
Entry("with two memberOf lambda filters combined with and",
"memberOf/any(n:n/id eq 25cb7bc0-3168-4a0c-adbe-396f478ad494) and memberOf/any(n:n/id eq 2713f1d5-6822-42bd-ad56-9f6c55a3a8fa)",
http.StatusOK),
Entry("with two memberOf lambda filters combined with or",
"memberOf/any(n:n/id eq 25cb7bc0-3168-4a0c-adbe-396f478ad494) or memberOf/any(n:n/id eq 2713f1d5-6822-42bd-ad56-9f6c55a3a8fa)",
http.StatusOK),
Entry("with supported appRoleAssignments lambda filter property",
"appRoleAssignments/any(n:n/appRoleId eq 'some-appRoleAssignment-ID') and memberOf/any(n:n/id eq 2713f1d5-6822-42bd-ad56-9f6c55a3a8fa)",
http.StatusOK),