mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-12 00:47:33 +00:00
feat: list users scim v2 endpoint (#9187)
# Which Problems Are Solved - Adds support for the list users SCIM v2 endpoint # How the Problems Are Solved - Adds support for the list users SCIM v2 endpoints under `GET /scim/v2/{orgID}/Users` and `POST /scim/v2/{orgID}/Users/.search` # Additional Changes - adds a new function `SearchUserMetadataForUsers` to the query layer to query a metadata keyset for given user ids - adds a new function `NewUserMetadataExistsQuery` to the query layer to query a given metadata key value pair exists - adds a new function `CountUsers` to the query layer to count users without reading any rows - handle `ErrorAlreadyExists` as scim errors `uniqueness` - adds `NumberLessOrEqual` and `NumberGreaterOrEqual` query comparison methods - adds `BytesQuery` with `BytesEquals` and `BytesNotEquals` query comparison methods # Additional Context Part of #8140 Supported fields for scim filters: * `meta.created` * `meta.lastModified` * `id` * `username` * `name.familyName` * `name.givenName` * `emails` and `emails.value` * `active` only eq and ne * `externalId` only eq and ne
This commit is contained in:
144
internal/api/scim/resources/user_query_builder_test.go
Normal file
144
internal/api/scim/resources/user_query_builder_test.go
Normal file
@@ -0,0 +1,144 @@
|
||||
package resources
|
||||
|
||||
import (
|
||||
"context"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/muhlemmer/gu"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/scim/metadata"
|
||||
"github.com/zitadel/zitadel/internal/api/scim/resources/filter"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/query"
|
||||
"github.com/zitadel/zitadel/internal/test"
|
||||
)
|
||||
|
||||
func Test_buildMetadataQuery(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
key metadata.Key
|
||||
value *filter.CompValue
|
||||
op *filter.CompareOp
|
||||
want query.SearchQuery
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "equals",
|
||||
key: "foo",
|
||||
value: &filter.CompValue{StringValue: gu.Ptr("bar")},
|
||||
op: &filter.CompareOp{Equal: true},
|
||||
want: test.Must(query.NewUserMetadataExistsQuery("foo", []byte("bar"), query.TextEquals, query.BytesEquals)),
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "not equals",
|
||||
key: "foo",
|
||||
value: &filter.CompValue{StringValue: gu.Ptr("bar")},
|
||||
op: &filter.CompareOp{NotEqual: true},
|
||||
want: test.Must(query.NewUserMetadataExistsQuery("foo", []byte("bar"), query.TextEquals, query.BytesNotEquals)),
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "unsupported operator",
|
||||
key: "foo",
|
||||
value: &filter.CompValue{StringValue: gu.Ptr("bar")},
|
||||
op: &filter.CompareOp{StartsWith: true},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "unsupported comparison value",
|
||||
key: "foo",
|
||||
value: &filter.CompValue{Int: gu.Ptr(10)},
|
||||
op: &filter.CompareOp{Equal: true},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := buildMetadataQuery(context.Background(), tt.key, tt.value, tt.op)
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
return
|
||||
}
|
||||
|
||||
require.NoError(t, err)
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("buildMetadataQuery() got = %#v, want %#v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_buildActiveUserStateQuery(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
compareValue *filter.CompValue
|
||||
compOp *filter.CompareOp
|
||||
want query.SearchQuery
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "eq true",
|
||||
compareValue: &filter.CompValue{BooleanTrue: true},
|
||||
compOp: &filter.CompareOp{Equal: true},
|
||||
want: test.Must(query.NewOrQuery(
|
||||
test.Must(query.NewNumberQuery(query.UserStateCol, int32(domain.UserStateInitial), query.NumberEquals)),
|
||||
test.Must(query.NewNumberQuery(query.UserStateCol, int32(domain.UserStateActive), query.NumberEquals)),
|
||||
)),
|
||||
},
|
||||
{
|
||||
name: "eq false",
|
||||
compareValue: &filter.CompValue{BooleanFalse: true},
|
||||
compOp: &filter.CompareOp{Equal: true},
|
||||
want: test.Must(query.NewAndQuery(
|
||||
test.Must(query.NewNumberQuery(query.UserStateCol, int32(domain.UserStateInitial), query.NumberNotEquals)),
|
||||
test.Must(query.NewNumberQuery(query.UserStateCol, int32(domain.UserStateActive), query.NumberNotEquals)),
|
||||
)),
|
||||
},
|
||||
{
|
||||
name: "ne true",
|
||||
compareValue: &filter.CompValue{BooleanTrue: true},
|
||||
compOp: &filter.CompareOp{NotEqual: true},
|
||||
want: test.Must(query.NewAndQuery(
|
||||
test.Must(query.NewNumberQuery(query.UserStateCol, int32(domain.UserStateInitial), query.NumberNotEquals)),
|
||||
test.Must(query.NewNumberQuery(query.UserStateCol, int32(domain.UserStateActive), query.NumberNotEquals)),
|
||||
)),
|
||||
},
|
||||
{
|
||||
name: "ne false",
|
||||
compareValue: &filter.CompValue{BooleanTrue: true},
|
||||
compOp: &filter.CompareOp{Equal: true},
|
||||
want: test.Must(query.NewOrQuery(
|
||||
test.Must(query.NewNumberQuery(query.UserStateCol, int32(domain.UserStateInitial), query.NumberEquals)),
|
||||
test.Must(query.NewNumberQuery(query.UserStateCol, int32(domain.UserStateActive), query.NumberEquals)),
|
||||
)),
|
||||
},
|
||||
{
|
||||
name: "invalid operator",
|
||||
compareValue: &filter.CompValue{BooleanTrue: true},
|
||||
compOp: &filter.CompareOp{StartsWith: true},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid comp value",
|
||||
compareValue: &filter.CompValue{StringValue: gu.Ptr("foo")},
|
||||
compOp: &filter.CompareOp{Equal: true},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := buildActiveUserStateQuery(context.Background(), tt.compareValue, tt.compOp)
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
return
|
||||
}
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.Equalf(t, tt.want, got, "buildActiveUserStateQuery(%#v, %#v)", tt.compareValue, tt.compOp)
|
||||
})
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user