graph: Initial support for $filter in /users (#5533)
This adds some initial support for using $filter (as defined in the odata spec) on the /users endpoint. Currently the following filters are supported: A single filter on `id` property of the `memberOf` relation of users. To list all users that are members of a specific group: ``` curl 'https://localhost:9200/graph/v1.0/users?$filter=memberOf/any(m:m/id eq '262982c1-2362-4afa-bfdf-8cbfef64a06e') ``` A logical AND filteri on the `id` property of the `memberOf` relation of users. `$filter=memberOf/any(m:m/id eq 262982c1-2362-4afa-bfdf-8cbfef64a06e) and memberOf/any(m:m/id eq 6040aa17-9c64-4fef-9bd0-77234d71bad0)` This will cause at least two queries on the identity backend. The `and` operation is performed locally. Closes: #5487
This commit is contained in:
@@ -35,7 +35,8 @@ type Backend interface {
|
|||||||
DeleteGroup(ctx context.Context, id string) error
|
DeleteGroup(ctx context.Context, id string) error
|
||||||
GetGroup(ctx context.Context, nameOrID string, queryParam url.Values) (*libregraph.Group, error)
|
GetGroup(ctx context.Context, nameOrID string, queryParam url.Values) (*libregraph.Group, error)
|
||||||
GetGroups(ctx context.Context, queryParam url.Values) ([]*libregraph.Group, error)
|
GetGroups(ctx context.Context, queryParam url.Values) ([]*libregraph.Group, error)
|
||||||
GetGroupMembers(ctx context.Context, id string) ([]*libregraph.User, error)
|
// GetGroupMembers list all members of a group
|
||||||
|
GetGroupMembers(ctx context.Context, id string, oreq *godata.GoDataRequest) ([]*libregraph.User, error)
|
||||||
// AddMembersToGroup adds new members (reference by a slice of IDs) to supplied group in the identity backend.
|
// AddMembersToGroup adds new members (reference by a slice of IDs) to supplied group in the identity backend.
|
||||||
AddMembersToGroup(ctx context.Context, groupID string, memberID []string) error
|
AddMembersToGroup(ctx context.Context, groupID string, memberID []string) error
|
||||||
// RemoveMemberFromGroup removes a single member (by ID) from a group
|
// RemoveMemberFromGroup removes a single member (by ID) from a group
|
||||||
|
|||||||
@@ -193,7 +193,7 @@ func (i *CS3) DeleteGroup(ctx context.Context, id string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetGroupMembers implements the Backend Interface. It's currently not supported for the CS3 backend
|
// GetGroupMembers implements the Backend Interface. It's currently not supported for the CS3 backend
|
||||||
func (i *CS3) GetGroupMembers(ctx context.Context, groupID string) ([]*libregraph.User, error) {
|
func (i *CS3) GetGroupMembers(ctx context.Context, groupID string, _ *godata.GoDataRequest) ([]*libregraph.User, error) {
|
||||||
return nil, errorcode.New(errorcode.NotSupported, "not implemented")
|
return nil, errorcode.New(errorcode.NotSupported, "not implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/CiscoM31/godata"
|
||||||
"github.com/go-ldap/ldap/v3"
|
"github.com/go-ldap/ldap/v3"
|
||||||
"github.com/gofrs/uuid"
|
"github.com/gofrs/uuid"
|
||||||
ldapdn "github.com/libregraph/idm/pkg/ldapdn"
|
ldapdn "github.com/libregraph/idm/pkg/ldapdn"
|
||||||
@@ -134,9 +135,15 @@ func (i *LDAP) GetGroups(ctx context.Context, queryParam url.Values) ([]*libregr
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GetGroupMembers implements the Backend Interface for the LDAP Backend
|
// GetGroupMembers implements the Backend Interface for the LDAP Backend
|
||||||
func (i *LDAP) GetGroupMembers(ctx context.Context, groupID string) ([]*libregraph.User, error) {
|
func (i *LDAP) GetGroupMembers(ctx context.Context, groupID string, req *godata.GoDataRequest) ([]*libregraph.User, error) {
|
||||||
logger := i.logger.SubloggerWithRequestID(ctx)
|
logger := i.logger.SubloggerWithRequestID(ctx)
|
||||||
logger.Debug().Str("backend", "ldap").Msg("GetGroupMembers")
|
logger.Debug().Str("backend", "ldap").Msg("GetGroupMembers")
|
||||||
|
|
||||||
|
exp, err := GetExpandValues(req.Query)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
e, err := i.getLDAPGroupByNameOrID(groupID, true)
|
e, err := i.getLDAPGroupByNameOrID(groupID, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -149,6 +156,13 @@ func (i *LDAP) GetGroupMembers(ctx context.Context, groupID string) ([]*libregra
|
|||||||
}
|
}
|
||||||
for _, member := range memberEntries {
|
for _, member := range memberEntries {
|
||||||
if u := i.createUserModelFromLDAP(member); u != nil {
|
if u := i.createUserModelFromLDAP(member); u != nil {
|
||||||
|
if slices.Contains(exp, "memberOf") {
|
||||||
|
userGroups, err := i.getGroupsForUser(member.DN)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
u.MemberOf = i.groupsFromLDAPEntries(userGroups)
|
||||||
|
}
|
||||||
result = append(result, u)
|
result = append(result, u)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -130,13 +130,13 @@ func (_m *Backend) GetGroup(ctx context.Context, nameOrID string, queryParam url
|
|||||||
return r0, r1
|
return r0, r1
|
||||||
}
|
}
|
||||||
|
|
||||||
// GetGroupMembers provides a mock function with given fields: ctx, id
|
// GetGroupMembers provides a mock function with given fields: ctx, id, oreq
|
||||||
func (_m *Backend) GetGroupMembers(ctx context.Context, id string) ([]*libregraph.User, error) {
|
func (_m *Backend) GetGroupMembers(ctx context.Context, id string, oreq *godata.GoDataRequest) ([]*libregraph.User, error) {
|
||||||
ret := _m.Called(ctx, id)
|
ret := _m.Called(ctx, id, oreq)
|
||||||
|
|
||||||
var r0 []*libregraph.User
|
var r0 []*libregraph.User
|
||||||
if rf, ok := ret.Get(0).(func(context.Context, string) []*libregraph.User); ok {
|
if rf, ok := ret.Get(0).(func(context.Context, string, *godata.GoDataRequest) []*libregraph.User); ok {
|
||||||
r0 = rf(ctx, id)
|
r0 = rf(ctx, id, oreq)
|
||||||
} else {
|
} else {
|
||||||
if ret.Get(0) != nil {
|
if ret.Get(0) != nil {
|
||||||
r0 = ret.Get(0).([]*libregraph.User)
|
r0 = ret.Get(0).([]*libregraph.User)
|
||||||
@@ -144,8 +144,8 @@ func (_m *Backend) GetGroupMembers(ctx context.Context, id string) ([]*libregrap
|
|||||||
}
|
}
|
||||||
|
|
||||||
var r1 error
|
var r1 error
|
||||||
if rf, ok := ret.Get(1).(func(context.Context, string) error); ok {
|
if rf, ok := ret.Get(1).(func(context.Context, string, *godata.GoDataRequest) error); ok {
|
||||||
r1 = rf(ctx, id)
|
r1 = rf(ctx, id, oreq)
|
||||||
} else {
|
} else {
|
||||||
r1 = ret.Error(1)
|
r1 = ret.Error(1)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -259,6 +259,7 @@ func (g Graph) DeleteGroup(w http.ResponseWriter, r *http.Request) {
|
|||||||
func (g Graph) GetGroupMembers(w http.ResponseWriter, r *http.Request) {
|
func (g Graph) GetGroupMembers(w http.ResponseWriter, r *http.Request) {
|
||||||
logger := g.logger.SubloggerWithRequestID(r.Context())
|
logger := g.logger.SubloggerWithRequestID(r.Context())
|
||||||
logger.Info().Msg("calling get group members")
|
logger.Info().Msg("calling get group members")
|
||||||
|
sanitizedPath := strings.TrimPrefix(r.URL.Path, "/graph/v1.0/")
|
||||||
groupID := chi.URLParam(r, "groupID")
|
groupID := chi.URLParam(r, "groupID")
|
||||||
groupID, err := url.PathUnescape(groupID)
|
groupID, err := url.PathUnescape(groupID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -273,8 +274,15 @@ func (g Graph) GetGroupMembers(w http.ResponseWriter, r *http.Request) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
odataReq, err := godata.ParseRequest(r.Context(), sanitizedPath, r.URL.Query())
|
||||||
|
if err != nil {
|
||||||
|
logger.Debug().Err(err).Interface("query", r.URL.Query()).Msg("could not get users: query error")
|
||||||
|
errorcode.InvalidRequest.Render(w, r, http.StatusBadRequest, err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
logger.Debug().Str("id", groupID).Msg("calling get group members on backend")
|
logger.Debug().Str("id", groupID).Msg("calling get group members on backend")
|
||||||
members, err := g.identityBackend.GetGroupMembers(r.Context(), groupID)
|
members, err := g.identityBackend.GetGroupMembers(r.Context(), groupID, odataReq)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Debug().Err(err).Msg("could not get group members: backend error")
|
logger.Debug().Err(err).Msg("could not get group members: backend error")
|
||||||
var errcode errorcode.Error
|
var errcode errorcode.Error
|
||||||
|
|||||||
@@ -201,13 +201,25 @@ func (g Graph) GetUsers(w http.ResponseWriter, r *http.Request) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
logger.Debug().Interface("query", r.URL.Query()).Msg("calling get users on backend")
|
logger.Debug().Interface("query", r.URL.Query()).Msg("calling get users on backend")
|
||||||
users, err := g.identityBackend.GetUsers(r.Context(), odataReq)
|
|
||||||
|
var users []*libregraph.User
|
||||||
|
|
||||||
|
if odataReq.Query.Filter != nil {
|
||||||
|
users, err = g.applyUserFilter(r.Context(), odataReq, nil)
|
||||||
|
} else {
|
||||||
|
users, err = g.identityBackend.GetUsers(r.Context(), odataReq)
|
||||||
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Debug().Err(err).Interface("query", r.URL.Query()).Msg("could not get users from backend")
|
logger.Debug().Err(err).Interface("query", r.URL.Query()).Msg("could not get users from backend")
|
||||||
var errcode errorcode.Error
|
var errcode errorcode.Error
|
||||||
if errors.As(err, &errcode) {
|
var godataerr *godata.GoDataError
|
||||||
|
switch {
|
||||||
|
case errors.As(err, &errcode):
|
||||||
errcode.Render(w, r)
|
errcode.Render(w, r)
|
||||||
} else {
|
case errors.As(err, &godataerr):
|
||||||
|
errorcode.GeneralException.Render(w, r, godataerr.ResponseCode, err.Error())
|
||||||
|
default:
|
||||||
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error())
|
errorcode.GeneralException.Render(w, r, http.StatusInternalServerError, err.Error())
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -0,0 +1,153 @@
|
|||||||
|
package svc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/CiscoM31/godata"
|
||||||
|
libregraph "github.com/owncloud/libre-graph-api-go"
|
||||||
|
)
|
||||||
|
|
||||||
|
func invalidFilterError() error {
|
||||||
|
return godata.BadRequestError("invalid filter")
|
||||||
|
}
|
||||||
|
|
||||||
|
func unsupportedFilterError() error {
|
||||||
|
return godata.NotImplementedError("unsupported filter")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g Graph) applyUserFilter(ctx context.Context, req *godata.GoDataRequest, root *godata.ParseNode) (users []*libregraph.User, err error) {
|
||||||
|
logger := g.logger.SubloggerWithRequestID(ctx)
|
||||||
|
|
||||||
|
if root == nil {
|
||||||
|
root = req.Query.Filter.Tree
|
||||||
|
}
|
||||||
|
|
||||||
|
switch root.Token.Type {
|
||||||
|
case godata.ExpressionTokenLambdaNav:
|
||||||
|
return g.applyFilterLambda(ctx, req, root.Children)
|
||||||
|
case godata.ExpressionTokenLogical:
|
||||||
|
return g.applyFilterLogical(ctx, req, root)
|
||||||
|
}
|
||||||
|
logger.Debug().Str("filter", req.Query.Filter.RawValue).Msg("filter is not supported")
|
||||||
|
return users, unsupportedFilterError()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g Graph) applyFilterLogical(ctx context.Context, req *godata.GoDataRequest, root *godata.ParseNode) (users []*libregraph.User, err error) {
|
||||||
|
logger := g.logger.SubloggerWithRequestID(ctx)
|
||||||
|
if root.Token.Type != godata.ExpressionTokenLogical {
|
||||||
|
return users, invalidFilterError()
|
||||||
|
}
|
||||||
|
|
||||||
|
switch root.Token.Value {
|
||||||
|
case "and":
|
||||||
|
// 'and' needs 2 operands
|
||||||
|
if len(root.Children) != 2 {
|
||||||
|
return users, invalidFilterError()
|
||||||
|
}
|
||||||
|
return g.applyFilterLogicalAnd(ctx, req, root.Children[0], root.Children[1])
|
||||||
|
}
|
||||||
|
logger.Debug().Str("Token", root.Token.Value).Msg("unsupported logical filter")
|
||||||
|
return users, unsupportedFilterError()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g Graph) applyFilterLogicalAnd(ctx context.Context, req *godata.GoDataRequest, operand1 *godata.ParseNode, operand2 *godata.ParseNode) (users []*libregraph.User, err error) {
|
||||||
|
logger := g.logger.SubloggerWithRequestID(ctx)
|
||||||
|
results := make([][]*libregraph.User, 0, 2)
|
||||||
|
|
||||||
|
for _, node := range []*godata.ParseNode{operand1, operand2} {
|
||||||
|
res, err := g.applyUserFilter(ctx, req, node)
|
||||||
|
if err != nil {
|
||||||
|
return users, err
|
||||||
|
}
|
||||||
|
logger.Debug().Interface("subfilter", res).Msg("result part")
|
||||||
|
results = append(results, res)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 'results' contains two slices of libregraph.Users now turn one of them
|
||||||
|
// into a map for efficiently getting the intersection of both slices
|
||||||
|
userSet := userSliceToMap(results[0])
|
||||||
|
var filteredUsers []*libregraph.User
|
||||||
|
for _, user := range results[1] {
|
||||||
|
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 {
|
||||||
|
return users, invalidFilterError()
|
||||||
|
}
|
||||||
|
// We only support memberOf/any queries for now
|
||||||
|
if nodes[0].Token.Type != godata.ExpressionTokenLiteral || nodes[0].Token.Value != "memberOf" {
|
||||||
|
logger.Debug().Str("Token", nodes[0].Token.Value).Msg("unsupported relation for lambda filter")
|
||||||
|
return users, unsupportedFilterError()
|
||||||
|
}
|
||||||
|
if nodes[1].Token.Type != godata.ExpressionTokenLambda || nodes[1].Token.Value != "any" {
|
||||||
|
logger.Debug().Str("Token", nodes[1].Token.Value).Msg("unsupported lambda filter")
|
||||||
|
return users, unsupportedFilterError()
|
||||||
|
}
|
||||||
|
return g.applyLambdaMemberOfAny(ctx, req, nodes[1].Children)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g Graph) applyLambdaMemberOfAny(ctx context.Context, req *godata.GoDataRequest, nodes []*godata.ParseNode) (users []*libregraph.User, err error) {
|
||||||
|
if len(nodes) != 2 {
|
||||||
|
return users, invalidFilterError()
|
||||||
|
}
|
||||||
|
|
||||||
|
// First element is the "name" of the lambda function's parameter
|
||||||
|
if nodes[0].Token.Type != godata.ExpressionTokenLiteral {
|
||||||
|
return users, invalidFilterError()
|
||||||
|
}
|
||||||
|
|
||||||
|
// We only support the 'eq' expression for now
|
||||||
|
if nodes[1].Token.Type != godata.ExpressionTokenLogical || nodes[1].Token.Value != "eq" {
|
||||||
|
return users, unsupportedFilterError()
|
||||||
|
}
|
||||||
|
return g.applyMemberOfEq(ctx, req, nodes[1].Children)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g Graph) applyMemberOfEq(ctx context.Context, req *godata.GoDataRequest, nodes []*godata.ParseNode) (users []*libregraph.User, err error) {
|
||||||
|
logger := g.logger.SubloggerWithRequestID(ctx)
|
||||||
|
if len(nodes) != 2 {
|
||||||
|
return users, invalidFilterError()
|
||||||
|
}
|
||||||
|
|
||||||
|
if nodes[0].Token.Type != godata.ExpressionTokenNav {
|
||||||
|
return users, invalidFilterError()
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(nodes[0].Children) != 2 {
|
||||||
|
return users, invalidFilterError()
|
||||||
|
}
|
||||||
|
|
||||||
|
switch nodes[0].Children[1].Token.Value {
|
||||||
|
case "id":
|
||||||
|
var filterValue string
|
||||||
|
switch nodes[1].Token.Type {
|
||||||
|
case godata.ExpressionTokenGuid:
|
||||||
|
filterValue = nodes[1].Token.Value
|
||||||
|
case godata.ExpressionTokenString:
|
||||||
|
// unquote
|
||||||
|
filterValue = strings.Trim(nodes[1].Token.Value, "'")
|
||||||
|
default:
|
||||||
|
return users, unsupportedFilterError()
|
||||||
|
}
|
||||||
|
logger.Debug().Str("property", nodes[0].Children[1].Token.Value).Str("value", filterValue).Msg("Filtering memberOf by group id")
|
||||||
|
return g.identityBackend.GetGroupMembers(ctx, filterValue, req)
|
||||||
|
default:
|
||||||
|
return users, unsupportedFilterError()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func userSliceToMap(users []*libregraph.User) map[string]*libregraph.User {
|
||||||
|
resMap := make(map[string]*libregraph.User, len(users))
|
||||||
|
for _, user := range users {
|
||||||
|
resMap[user.GetId()] = user
|
||||||
|
}
|
||||||
|
return resMap
|
||||||
|
}
|
||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
userv1beta1 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
|
userv1beta1 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
|
||||||
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
|
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
|
||||||
@@ -324,6 +325,43 @@ var _ = Describe("Users", func() {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
DescribeTable("GetUsers handles unsupported or invalid filters",
|
||||||
|
func(filter string, status int) {
|
||||||
|
r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/users?$filter="+url.QueryEscape(filter), nil)
|
||||||
|
svc.GetUsers(rr, r)
|
||||||
|
|
||||||
|
Expect(rr.Code).To(Equal(status))
|
||||||
|
},
|
||||||
|
Entry("with invalid filter", "invalid", http.StatusBadRequest),
|
||||||
|
Entry("with unsupported filter for user property", "mail eq 'unsupported'", http.StatusNotImplemented),
|
||||||
|
Entry("with unsupported filter operation", "mail add 10", http.StatusNotImplemented),
|
||||||
|
Entry("with unsupported logical operation", "memberOf/any(n:n/id eq 1) or memberOf/any(n:n/id eq 2)", http.StatusNotImplemented),
|
||||||
|
Entry("with unsupported lambda query ", `drives/any(n:n/id eq '1')`, http.StatusNotImplemented),
|
||||||
|
Entry("with unsupported lambda token ", "memberOf/all(n:n/id eq 1)", http.StatusNotImplemented),
|
||||||
|
Entry("with unsupported filter operation ", "memberOf/any(n:n/id ne 1)", http.StatusNotImplemented),
|
||||||
|
Entry("with unsupported filter operand type", "memberOf/any(n:n/id eq 1)", http.StatusNotImplemented),
|
||||||
|
Entry("with unsupported lambda filter property", "memberOf/any(n:n/name eq 'name')", http.StatusNotImplemented),
|
||||||
|
)
|
||||||
|
|
||||||
|
DescribeTable("With a valid memberOf filter",
|
||||||
|
func(filter string, status int) {
|
||||||
|
user := &libregraph.User{}
|
||||||
|
user.SetId("25cb7bc0-3168-4a0c-adbe-396f478ad494")
|
||||||
|
users := []*libregraph.User{user}
|
||||||
|
identityBackend.On("GetGroupMembers", mock.Anything, "25cb7bc0-3168-4a0c-adbe-396f478ad494", mock.Anything).Return(users, nil)
|
||||||
|
identityBackend.On("GetGroupMembers", mock.Anything, "2713f1d5-6822-42bd-ad56-9f6c55a3a8fa", mock.Anything).Return([]*libregraph.User{}, nil)
|
||||||
|
r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/users?$filter="+url.QueryEscape(filter), nil)
|
||||||
|
svc.GetUsers(rr, r)
|
||||||
|
|
||||||
|
Expect(rr.Code).To(Equal(status))
|
||||||
|
},
|
||||||
|
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 two memberOf lambda filters",
|
||||||
|
"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),
|
||||||
|
)
|
||||||
|
|
||||||
Describe("GetUser", func() {
|
Describe("GetUser", func() {
|
||||||
It("handles missing userids", func() {
|
It("handles missing userids", func() {
|
||||||
r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/users", nil)
|
r := httptest.NewRequest(http.MethodGet, "/graph/v1.0/users", nil)
|
||||||
@@ -345,6 +383,7 @@ var _ = Describe("Users", func() {
|
|||||||
|
|
||||||
Expect(rr.Code).To(Equal(http.StatusOK))
|
Expect(rr.Code).To(Equal(http.StatusOK))
|
||||||
data, err := io.ReadAll(rr.Body)
|
data, err := io.ReadAll(rr.Body)
|
||||||
|
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
responseUser := &libregraph.User{}
|
responseUser := &libregraph.User{}
|
||||||
err = json.Unmarshal(data, &responseUser)
|
err = json.Unmarshal(data, &responseUser)
|
||||||
|
|||||||
Reference in New Issue
Block a user