mirror of
https://github.com/zitadel/zitadel.git
synced 2025-03-01 14:07:24 +00:00

# 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
145 lines
4.4 KiB
Go
145 lines
4.4 KiB
Go
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)
|
|
})
|
|
}
|
|
}
|