mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-11 21:27:42 +00:00
chore!: Introduce ZITADEL v3 (#9645)
This PR summarizes multiple changes specifically only available with ZITADEL v3: - feat: Web Keys management (https://github.com/zitadel/zitadel/pull/9526) - fix(cmd): ensure proper working of mirror (https://github.com/zitadel/zitadel/pull/9509) - feat(Authz): system user support for permission check v2 (https://github.com/zitadel/zitadel/pull/9640) - chore(license): change from Apache to AGPL (https://github.com/zitadel/zitadel/pull/9597) - feat(console): list v2 sessions (https://github.com/zitadel/zitadel/pull/9539) - fix(console): add loginV2 feature flag (https://github.com/zitadel/zitadel/pull/9682) - fix(feature flags): allow reading "own" flags (https://github.com/zitadel/zitadel/pull/9649) - feat(console): add Actions V2 UI (https://github.com/zitadel/zitadel/pull/9591) BREAKING CHANGE - feat(webkey): migrate to v2beta API (https://github.com/zitadel/zitadel/pull/9445) - chore!: remove CockroachDB Support (https://github.com/zitadel/zitadel/pull/9444) - feat(actions): migrate to v2beta API (https://github.com/zitadel/zitadel/pull/9489) --------- Co-authored-by: Livio Spring <livio.a@gmail.com> Co-authored-by: Stefan Benz <46600784+stebenz@users.noreply.github.com> Co-authored-by: Silvan <27845747+adlerhurst@users.noreply.github.com> Co-authored-by: Ramon <mail@conblem.me> Co-authored-by: Elio Bischof <elio@zitadel.com> Co-authored-by: Kenta Yamaguchi <56732734+KEY60228@users.noreply.github.com> Co-authored-by: Harsha Reddy <harsha.reddy@klaviyo.com> Co-authored-by: Livio Spring <livio@zitadel.com> Co-authored-by: Max Peintner <max@caos.ch> Co-authored-by: Iraq <66622793+kkrime@users.noreply.github.com> Co-authored-by: Florian Forster <florian@zitadel.com> Co-authored-by: Tim Möhlmann <tim+github@zitadel.com> Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> Co-authored-by: Max Peintner <peintnerm@gmail.com>
This commit is contained in:
@@ -5,444 +5,438 @@ package integration_test
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"slices"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/brianvoe/gofakeit/v6"
|
||||
"github.com/muhlemmer/gu"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/scim/resources"
|
||||
"github.com/zitadel/zitadel/internal/api/scim/schemas"
|
||||
"github.com/zitadel/zitadel/internal/integration"
|
||||
"github.com/zitadel/zitadel/internal/integration/scim"
|
||||
"github.com/zitadel/zitadel/internal/test"
|
||||
"github.com/zitadel/zitadel/pkg/grpc/object/v2"
|
||||
user_v2 "github.com/zitadel/zitadel/pkg/grpc/user/v2"
|
||||
)
|
||||
|
||||
var totalCountOfHumanUsers = 13
|
||||
|
||||
func TestListUser(t *testing.T) {
|
||||
createdUserIDs := createUsers(t, CTX, Instance.DefaultOrg.Id)
|
||||
defer func() {
|
||||
// only the full user needs to be deleted, all others have random identification data
|
||||
// fullUser is always the first one.
|
||||
_, err := Instance.Client.UserV2.DeleteUser(CTX, &user_v2.DeleteUserRequest{
|
||||
UserId: createdUserIDs[0],
|
||||
})
|
||||
require.NoError(t, err)
|
||||
}()
|
||||
/*
|
||||
func TestListUser(t *testing.T) {
|
||||
createdUserIDs := createUsers(t, CTX, Instance.DefaultOrg.Id)
|
||||
defer func() {
|
||||
// only the full user needs to be deleted, all others have random identification data
|
||||
// fullUser is always the first one.
|
||||
_, err := Instance.Client.UserV2.DeleteUser(CTX, &user_v2.DeleteUserRequest{
|
||||
UserId: createdUserIDs[0],
|
||||
})
|
||||
require.NoError(t, err)
|
||||
}()
|
||||
|
||||
// secondary organization with same set of users,
|
||||
// these should never be modified.
|
||||
// This allows testing list requests without filters.
|
||||
iamOwnerCtx := Instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
|
||||
secondaryOrg := Instance.CreateOrganization(iamOwnerCtx, gofakeit.Name(), gofakeit.Email())
|
||||
secondaryOrgCreatedUserIDs := createUsers(t, iamOwnerCtx, secondaryOrg.OrganizationId)
|
||||
// secondary organization with same set of users,
|
||||
// these should never be modified.
|
||||
// This allows testing list requests without filters.
|
||||
iamOwnerCtx := Instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
|
||||
secondaryOrg := Instance.CreateOrganization(iamOwnerCtx, gofakeit.Name(), gofakeit.Email())
|
||||
secondaryOrgCreatedUserIDs := createUsers(t, iamOwnerCtx, secondaryOrg.OrganizationId)
|
||||
|
||||
testsInitializedUtc := time.Now().UTC()
|
||||
testsInitializedUtc := time.Now().UTC()
|
||||
|
||||
// Wait one second to ensure a change in the least significant value of the timestamp.
|
||||
time.Sleep(time.Second)
|
||||
// Wait one second to ensure a change in the least significant value of the timestamp.
|
||||
time.Sleep(time.Second)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
ctx context.Context
|
||||
orgID string
|
||||
req *scim.ListRequest
|
||||
prepare func(require.TestingT) *scim.ListRequest
|
||||
wantErr bool
|
||||
errorStatus int
|
||||
errorType string
|
||||
assert func(assert.TestingT, *scim.ListResponse[*resources.ScimUser])
|
||||
cleanup func(require.TestingT)
|
||||
}{
|
||||
{
|
||||
name: "not authenticated",
|
||||
ctx: context.Background(),
|
||||
req: new(scim.ListRequest),
|
||||
wantErr: true,
|
||||
errorStatus: http.StatusUnauthorized,
|
||||
},
|
||||
{
|
||||
name: "no permissions",
|
||||
ctx: Instance.WithAuthorization(CTX, integration.UserTypeNoPermission),
|
||||
req: new(scim.ListRequest),
|
||||
wantErr: true,
|
||||
errorStatus: http.StatusNotFound,
|
||||
},
|
||||
{
|
||||
name: "unknown sort order",
|
||||
req: &scim.ListRequest{
|
||||
SortBy: gu.Ptr("id"),
|
||||
SortOrder: gu.Ptr(scim.ListRequestSortOrder("fooBar")),
|
||||
tests := []struct {
|
||||
name string
|
||||
ctx context.Context
|
||||
orgID string
|
||||
req *scim.ListRequest
|
||||
prepare func(require.TestingT) *scim.ListRequest
|
||||
wantErr bool
|
||||
errorStatus int
|
||||
errorType string
|
||||
assert func(assert.TestingT, *scim.ListResponse[*resources.ScimUser])
|
||||
cleanup func(require.TestingT)
|
||||
}{
|
||||
{
|
||||
name: "not authenticated",
|
||||
ctx: context.Background(),
|
||||
req: new(scim.ListRequest),
|
||||
wantErr: true,
|
||||
errorStatus: http.StatusUnauthorized,
|
||||
},
|
||||
wantErr: true,
|
||||
errorType: "invalidValue",
|
||||
},
|
||||
{
|
||||
name: "unknown sort field",
|
||||
req: &scim.ListRequest{
|
||||
SortBy: gu.Ptr("fooBar"),
|
||||
{
|
||||
name: "no permissions",
|
||||
ctx: Instance.WithAuthorization(CTX, integration.UserTypeNoPermission),
|
||||
req: new(scim.ListRequest),
|
||||
wantErr: true,
|
||||
errorStatus: http.StatusNotFound,
|
||||
},
|
||||
wantErr: true,
|
||||
errorType: "invalidValue",
|
||||
},
|
||||
{
|
||||
name: "custom sort field",
|
||||
req: &scim.ListRequest{
|
||||
SortBy: gu.Ptr("externalid"),
|
||||
{
|
||||
name: "unknown sort order",
|
||||
req: &scim.ListRequest{
|
||||
SortBy: gu.Ptr("id"),
|
||||
SortOrder: gu.Ptr(scim.ListRequestSortOrder("fooBar")),
|
||||
},
|
||||
wantErr: true,
|
||||
errorType: "invalidValue",
|
||||
},
|
||||
wantErr: true,
|
||||
errorType: "invalidValue",
|
||||
},
|
||||
{
|
||||
name: "unknown filter field",
|
||||
req: &scim.ListRequest{
|
||||
Filter: gu.Ptr(`fooBar eq "10"`),
|
||||
{
|
||||
name: "unknown sort field",
|
||||
req: &scim.ListRequest{
|
||||
SortBy: gu.Ptr("fooBar"),
|
||||
},
|
||||
wantErr: true,
|
||||
errorType: "invalidValue",
|
||||
},
|
||||
wantErr: true,
|
||||
errorType: "invalidFilter",
|
||||
},
|
||||
{
|
||||
name: "invalid filter",
|
||||
req: &scim.ListRequest{
|
||||
Filter: gu.Ptr(`fooBarBaz`),
|
||||
{
|
||||
name: "custom sort field",
|
||||
req: &scim.ListRequest{
|
||||
SortBy: gu.Ptr("externalid"),
|
||||
},
|
||||
wantErr: true,
|
||||
errorType: "invalidValue",
|
||||
},
|
||||
wantErr: true,
|
||||
errorType: "invalidFilter",
|
||||
},
|
||||
{
|
||||
name: "list users without filter",
|
||||
// use other org, modifications of users happens only on primary org
|
||||
orgID: secondaryOrg.OrganizationId,
|
||||
ctx: iamOwnerCtx,
|
||||
req: new(scim.ListRequest),
|
||||
assert: func(t assert.TestingT, resp *scim.ListResponse[*resources.ScimUser]) {
|
||||
assert.Equal(t, 100, resp.ItemsPerPage)
|
||||
assert.Equal(t, totalCountOfHumanUsers, resp.TotalResults)
|
||||
assert.Equal(t, 1, resp.StartIndex)
|
||||
assert.Len(t, resp.Resources, totalCountOfHumanUsers)
|
||||
{
|
||||
name: "unknown filter field",
|
||||
req: &scim.ListRequest{
|
||||
Filter: gu.Ptr(`fooBar eq "10"`),
|
||||
},
|
||||
wantErr: true,
|
||||
errorType: "invalidFilter",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list paged sorted users without filter",
|
||||
// use other org, modifications of users happens only on primary org
|
||||
orgID: secondaryOrg.OrganizationId,
|
||||
ctx: iamOwnerCtx,
|
||||
req: &scim.ListRequest{
|
||||
Count: gu.Ptr(2),
|
||||
StartIndex: gu.Ptr(5),
|
||||
SortOrder: gu.Ptr(scim.ListRequestSortOrderAsc),
|
||||
SortBy: gu.Ptr("username"),
|
||||
{
|
||||
name: "invalid filter",
|
||||
req: &scim.ListRequest{
|
||||
Filter: gu.Ptr(`fooBarBaz`),
|
||||
},
|
||||
wantErr: true,
|
||||
errorType: "invalidFilter",
|
||||
},
|
||||
assert: func(t assert.TestingT, resp *scim.ListResponse[*resources.ScimUser]) {
|
||||
// sort the created users with usernames instead of creation date
|
||||
sortedResources := sortScimUserByUsername(resp.Resources)
|
||||
{
|
||||
name: "list users without filter",
|
||||
// use other org, modifications of users happens only on primary org
|
||||
orgID: secondaryOrg.OrganizationId,
|
||||
ctx: iamOwnerCtx,
|
||||
req: new(scim.ListRequest),
|
||||
assert: func(t assert.TestingT, resp *scim.ListResponse[*resources.ScimUser]) {
|
||||
assert.Equal(t, 100, resp.ItemsPerPage)
|
||||
assert.Equal(t, totalCountOfHumanUsers, resp.TotalResults)
|
||||
assert.Equal(t, 1, resp.StartIndex)
|
||||
assert.Len(t, resp.Resources, totalCountOfHumanUsers)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list paged sorted users without filter",
|
||||
// use other org, modifications of users happens only on primary org
|
||||
orgID: secondaryOrg.OrganizationId,
|
||||
ctx: iamOwnerCtx,
|
||||
req: &scim.ListRequest{
|
||||
Count: gu.Ptr(2),
|
||||
StartIndex: gu.Ptr(5),
|
||||
SortOrder: gu.Ptr(scim.ListRequestSortOrderAsc),
|
||||
SortBy: gu.Ptr("username"),
|
||||
},
|
||||
assert: func(t assert.TestingT, resp *scim.ListResponse[*resources.ScimUser]) {
|
||||
// sort the created users with usernames instead of creation date
|
||||
sortedResources := sortScimUserByUsername(resp.Resources)
|
||||
|
||||
assert.Equal(t, 2, resp.ItemsPerPage)
|
||||
assert.Equal(t, totalCountOfHumanUsers, resp.TotalResults)
|
||||
assert.Equal(t, 5, resp.StartIndex)
|
||||
assert.Len(t, sortedResources, 2)
|
||||
assert.True(t, strings.HasPrefix(sortedResources[0].UserName, "scim-username-1: "))
|
||||
assert.True(t, strings.HasPrefix(sortedResources[1].UserName, "scim-username-2: "))
|
||||
assert.Equal(t, 2, resp.ItemsPerPage)
|
||||
assert.Equal(t, totalCountOfHumanUsers, resp.TotalResults)
|
||||
assert.Equal(t, 5, resp.StartIndex)
|
||||
assert.Len(t, sortedResources, 2)
|
||||
assert.True(t, strings.HasPrefix(sortedResources[0].UserName, "scim-username-1: "), "got %q", resp.Resources[0].UserName)
|
||||
assert.True(t, strings.HasPrefix(sortedResources[1].UserName, "scim-username-2: "), "got %q", resp.Resources[1].UserName)
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list users with simple filter",
|
||||
req: &scim.ListRequest{
|
||||
Filter: gu.Ptr(`username sw "scim-username-1"`),
|
||||
{
|
||||
name: "list users with simple filter",
|
||||
req: &scim.ListRequest{
|
||||
Filter: gu.Ptr(`username sw "scim-username-1"`),
|
||||
},
|
||||
assert: func(t assert.TestingT, resp *scim.ListResponse[*resources.ScimUser]) {
|
||||
assert.Equal(t, 100, resp.ItemsPerPage)
|
||||
assert.Equal(t, 2, resp.TotalResults)
|
||||
assert.Equal(t, 1, resp.StartIndex)
|
||||
assert.Len(t, resp.Resources, 2)
|
||||
for _, resource := range resp.Resources {
|
||||
assert.True(t, strings.HasPrefix(resource.UserName, "scim-username-1"))
|
||||
}
|
||||
},
|
||||
},
|
||||
assert: func(t assert.TestingT, resp *scim.ListResponse[*resources.ScimUser]) {
|
||||
assert.Equal(t, 100, resp.ItemsPerPage)
|
||||
assert.Equal(t, 2, resp.TotalResults)
|
||||
assert.Equal(t, 1, resp.StartIndex)
|
||||
assert.Len(t, resp.Resources, 2)
|
||||
for _, resource := range resp.Resources {
|
||||
assert.True(t, strings.HasPrefix(resource.UserName, "scim-username-1"))
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list paged sorted users with filter",
|
||||
req: &scim.ListRequest{
|
||||
Count: gu.Ptr(5),
|
||||
StartIndex: gu.Ptr(1),
|
||||
SortOrder: gu.Ptr(scim.ListRequestSortOrderAsc),
|
||||
SortBy: gu.Ptr("username"),
|
||||
Filter: gu.Ptr(`emails sw "scim-email-1" and emails ew "@example.com"`),
|
||||
},
|
||||
assert: func(t assert.TestingT, resp *scim.ListResponse[*resources.ScimUser]) {
|
||||
// sort the created users with usernames instead of creation date
|
||||
sortedResources := sortScimUserByUsername(resp.Resources)
|
||||
{
|
||||
name: "list paged sorted users with filter",
|
||||
req: &scim.ListRequest{
|
||||
Count: gu.Ptr(5),
|
||||
StartIndex: gu.Ptr(1),
|
||||
SortOrder: gu.Ptr(scim.ListRequestSortOrderAsc),
|
||||
SortBy: gu.Ptr("username"),
|
||||
Filter: gu.Ptr(`emails sw "scim-email-1" and emails ew "@example.com"`),
|
||||
},
|
||||
assert: func(t assert.TestingT, resp *scim.ListResponse[*resources.ScimUser]) {
|
||||
// sort the created users with usernames instead of creation date
|
||||
sortedResources := sortScimUserByUsername(resp.Resources)
|
||||
|
||||
assert.Equal(t, 5, resp.ItemsPerPage)
|
||||
assert.Equal(t, 2, resp.TotalResults)
|
||||
assert.Equal(t, 1, resp.StartIndex)
|
||||
assert.Len(t, sortedResources, 2)
|
||||
for _, resource := range sortedResources {
|
||||
assert.True(t, strings.HasPrefix(resource.UserName, "scim-username-1"))
|
||||
assert.Len(t, resource.Emails, 1)
|
||||
assert.True(t, strings.HasPrefix(resource.Emails[0].Value, "scim-email-1"))
|
||||
assert.True(t, strings.HasSuffix(resource.Emails[0].Value, "@example.com"))
|
||||
}
|
||||
assert.Equal(t, 5, resp.ItemsPerPage)
|
||||
assert.Equal(t, 2, resp.TotalResults)
|
||||
assert.Equal(t, 1, resp.StartIndex)
|
||||
assert.Len(t, sortedResources, 2)
|
||||
for _, resource := range sortedResources {
|
||||
assert.True(t, strings.HasPrefix(resource.UserName, "scim-username-1"))
|
||||
assert.Len(t, resource.Emails, 1)
|
||||
assert.True(t, strings.HasPrefix(resource.Emails[0].Value, "scim-email-1"))
|
||||
assert.True(t, strings.HasSuffix(resource.Emails[0].Value, "@example.com"))
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list paged sorted users with filter as post",
|
||||
req: &scim.ListRequest{
|
||||
Schemas: []schemas.ScimSchemaType{schemas.IdSearchRequest},
|
||||
Count: gu.Ptr(5),
|
||||
StartIndex: gu.Ptr(1),
|
||||
SortOrder: gu.Ptr(scim.ListRequestSortOrderAsc),
|
||||
SortBy: gu.Ptr("username"),
|
||||
Filter: gu.Ptr(`emails sw "scim-email-1" and emails ew "@example.com"`),
|
||||
SendAsPost: true,
|
||||
},
|
||||
assert: func(t assert.TestingT, resp *scim.ListResponse[*resources.ScimUser]) {
|
||||
// sort the created users with usernames instead of creation date
|
||||
sortedResources := sortScimUserByUsername(resp.Resources)
|
||||
{
|
||||
name: "list paged sorted users with filter as post",
|
||||
req: &scim.ListRequest{
|
||||
Schemas: []schemas.ScimSchemaType{schemas.IdSearchRequest},
|
||||
Count: gu.Ptr(5),
|
||||
StartIndex: gu.Ptr(1),
|
||||
SortOrder: gu.Ptr(scim.ListRequestSortOrderAsc),
|
||||
SortBy: gu.Ptr("username"),
|
||||
Filter: gu.Ptr(`emails sw "scim-email-1" and emails ew "@example.com"`),
|
||||
SendAsPost: true,
|
||||
},
|
||||
assert: func(t assert.TestingT, resp *scim.ListResponse[*resources.ScimUser]) {
|
||||
// sort the created users with usernames instead of creation date
|
||||
sortedResources := sortScimUserByUsername(resp.Resources)
|
||||
|
||||
assert.Equal(t, 5, resp.ItemsPerPage)
|
||||
assert.Equal(t, 2, resp.TotalResults)
|
||||
assert.Equal(t, 1, resp.StartIndex)
|
||||
assert.Len(t, sortedResources, 2)
|
||||
for _, resource := range sortedResources {
|
||||
assert.True(t, strings.HasPrefix(resource.UserName, "scim-username-1"))
|
||||
assert.Len(t, resource.Emails, 1)
|
||||
assert.True(t, strings.HasPrefix(resource.Emails[0].Value, "scim-email-1"))
|
||||
assert.True(t, strings.HasSuffix(resource.Emails[0].Value, "@example.com"))
|
||||
}
|
||||
assert.Equal(t, 5, resp.ItemsPerPage)
|
||||
assert.Equal(t, 2, resp.TotalResults)
|
||||
assert.Equal(t, 1, resp.StartIndex)
|
||||
assert.Len(t, sortedResources, 2)
|
||||
for _, resource := range sortedResources {
|
||||
assert.True(t, strings.HasPrefix(resource.UserName, "scim-username-1"))
|
||||
assert.Len(t, resource.Emails, 1)
|
||||
assert.True(t, strings.HasPrefix(resource.Emails[0].Value, "scim-email-1"))
|
||||
assert.True(t, strings.HasSuffix(resource.Emails[0].Value, "@example.com"))
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "count users without filter",
|
||||
// use other org, modifications of users happens only on primary org
|
||||
orgID: secondaryOrg.OrganizationId,
|
||||
ctx: iamOwnerCtx,
|
||||
prepare: func(t require.TestingT) *scim.ListRequest {
|
||||
return &scim.ListRequest{
|
||||
Count: gu.Ptr(0),
|
||||
}
|
||||
{
|
||||
name: "count users without filter",
|
||||
// use other org, modifications of users happens only on primary org
|
||||
orgID: secondaryOrg.OrganizationId,
|
||||
ctx: iamOwnerCtx,
|
||||
prepare: func(t require.TestingT) *scim.ListRequest {
|
||||
return &scim.ListRequest{
|
||||
Count: gu.Ptr(0),
|
||||
}
|
||||
},
|
||||
assert: func(t assert.TestingT, resp *scim.ListResponse[*resources.ScimUser]) {
|
||||
assert.Equal(t, 0, resp.ItemsPerPage)
|
||||
assert.Equal(t, totalCountOfHumanUsers, resp.TotalResults)
|
||||
assert.Equal(t, 1, resp.StartIndex)
|
||||
assert.Len(t, resp.Resources, 0)
|
||||
},
|
||||
},
|
||||
assert: func(t assert.TestingT, resp *scim.ListResponse[*resources.ScimUser]) {
|
||||
assert.Equal(t, 0, resp.ItemsPerPage)
|
||||
assert.Equal(t, totalCountOfHumanUsers, resp.TotalResults)
|
||||
assert.Equal(t, 1, resp.StartIndex)
|
||||
assert.Len(t, resp.Resources, 0)
|
||||
{
|
||||
name: "list users with active filter",
|
||||
req: &scim.ListRequest{
|
||||
Filter: gu.Ptr(`active eq false`),
|
||||
},
|
||||
assert: func(t assert.TestingT, resp *scim.ListResponse[*resources.ScimUser]) {
|
||||
assert.Equal(t, 100, resp.ItemsPerPage)
|
||||
assert.Equal(t, 1, resp.TotalResults)
|
||||
assert.Equal(t, 1, resp.StartIndex)
|
||||
assert.Len(t, resp.Resources, 1)
|
||||
assert.True(t, strings.HasPrefix(resp.Resources[0].UserName, "scim-username-0"))
|
||||
assert.False(t, resp.Resources[0].Active.Bool())
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list users with active filter",
|
||||
req: &scim.ListRequest{
|
||||
Filter: gu.Ptr(`active eq false`),
|
||||
{
|
||||
name: "list users with externalid filter",
|
||||
req: &scim.ListRequest{
|
||||
Filter: gu.Ptr(`externalid eq "701984"`),
|
||||
},
|
||||
assert: func(t assert.TestingT, resp *scim.ListResponse[*resources.ScimUser]) {
|
||||
assert.Equal(t, 100, resp.ItemsPerPage)
|
||||
assert.Equal(t, 1, resp.TotalResults)
|
||||
assert.Equal(t, 1, resp.StartIndex)
|
||||
assert.Len(t, resp.Resources, 1)
|
||||
assert.Equal(t, resp.Resources[0].ExternalID, "701984")
|
||||
},
|
||||
},
|
||||
assert: func(t assert.TestingT, resp *scim.ListResponse[*resources.ScimUser]) {
|
||||
assert.Equal(t, 100, resp.ItemsPerPage)
|
||||
assert.Equal(t, 1, resp.TotalResults)
|
||||
assert.Equal(t, 1, resp.StartIndex)
|
||||
assert.Len(t, resp.Resources, 1)
|
||||
assert.True(t, strings.HasPrefix(resp.Resources[0].UserName, "scim-username-0"))
|
||||
assert.False(t, resp.Resources[0].Active.Bool())
|
||||
{
|
||||
name: "list users with externalid filter invalid operator",
|
||||
req: &scim.ListRequest{
|
||||
Filter: gu.Ptr(`externalid pr`),
|
||||
},
|
||||
wantErr: true,
|
||||
errorType: "invalidFilter",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list users with externalid filter",
|
||||
req: &scim.ListRequest{
|
||||
Filter: gu.Ptr(`externalid eq "701984"`),
|
||||
{
|
||||
name: "list users with externalid complex filter",
|
||||
req: &scim.ListRequest{
|
||||
Filter: gu.Ptr(`externalid eq "701984" and username eq "bjensen@example.com"`),
|
||||
},
|
||||
assert: func(t assert.TestingT, resp *scim.ListResponse[*resources.ScimUser]) {
|
||||
assert.Equal(t, 100, resp.ItemsPerPage)
|
||||
assert.Equal(t, 1, resp.TotalResults)
|
||||
assert.Equal(t, 1, resp.StartIndex)
|
||||
assert.Len(t, resp.Resources, 1)
|
||||
assert.Equal(t, resp.Resources[0].UserName, "bjensen@example.com")
|
||||
assert.Equal(t, resp.Resources[0].ExternalID, "701984")
|
||||
},
|
||||
},
|
||||
assert: func(t assert.TestingT, resp *scim.ListResponse[*resources.ScimUser]) {
|
||||
assert.Equal(t, 100, resp.ItemsPerPage)
|
||||
assert.Equal(t, 1, resp.TotalResults)
|
||||
assert.Equal(t, 1, resp.StartIndex)
|
||||
assert.Len(t, resp.Resources, 1)
|
||||
assert.Equal(t, resp.Resources[0].ExternalID, "701984")
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list users with externalid filter invalid operator",
|
||||
req: &scim.ListRequest{
|
||||
Filter: gu.Ptr(`externalid pr`),
|
||||
},
|
||||
wantErr: true,
|
||||
errorType: "invalidFilter",
|
||||
},
|
||||
{
|
||||
name: "list users with externalid complex filter",
|
||||
req: &scim.ListRequest{
|
||||
Filter: gu.Ptr(`externalid eq "701984" and username eq "bjensen@example.com"`),
|
||||
},
|
||||
assert: func(t assert.TestingT, resp *scim.ListResponse[*resources.ScimUser]) {
|
||||
assert.Equal(t, 100, resp.ItemsPerPage)
|
||||
assert.Equal(t, 1, resp.TotalResults)
|
||||
assert.Equal(t, 1, resp.StartIndex)
|
||||
assert.Len(t, resp.Resources, 1)
|
||||
assert.Equal(t, resp.Resources[0].UserName, "bjensen@example.com")
|
||||
assert.Equal(t, resp.Resources[0].ExternalID, "701984")
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "count users with filter",
|
||||
req: &scim.ListRequest{
|
||||
Count: gu.Ptr(0),
|
||||
Filter: gu.Ptr(`emails sw "scim-email-1" and emails ew "@example.com"`),
|
||||
},
|
||||
assert: func(t assert.TestingT, resp *scim.ListResponse[*resources.ScimUser]) {
|
||||
assert.Equal(t, 0, resp.ItemsPerPage)
|
||||
assert.Equal(t, 2, resp.TotalResults)
|
||||
assert.Equal(t, 1, resp.StartIndex)
|
||||
assert.Len(t, resp.Resources, 0)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list users with modification date filter",
|
||||
prepare: func(t require.TestingT) *scim.ListRequest {
|
||||
userID := createdUserIDs[len(createdUserIDs)-1] // use the last entry, as we use the others for other assertions
|
||||
_, err := Instance.Client.UserV2.UpdateHumanUser(CTX, &user_v2.UpdateHumanUserRequest{
|
||||
UserId: userID,
|
||||
|
||||
Profile: &user_v2.SetHumanProfile{
|
||||
GivenName: "scim-user-given-name-modified-0: " + gofakeit.FirstName(),
|
||||
FamilyName: "scim-user-family-name-modified-0: " + gofakeit.LastName(),
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
return &scim.ListRequest{
|
||||
// filter by id too to exclude other random users
|
||||
Filter: gu.Ptr(fmt.Sprintf(`id eq "%s" and meta.LASTMODIFIED gt "%s"`, userID, testsInitializedUtc.Format(time.RFC3339))),
|
||||
}
|
||||
},
|
||||
assert: func(t assert.TestingT, resp *scim.ListResponse[*resources.ScimUser]) {
|
||||
assert.Len(t, resp.Resources, 1)
|
||||
assert.Equal(t, resp.Resources[0].ID, createdUserIDs[len(createdUserIDs)-1])
|
||||
assert.True(t, strings.HasPrefix(resp.Resources[0].Name.FamilyName, "scim-user-family-name-modified-0:"))
|
||||
assert.True(t, strings.HasPrefix(resp.Resources[0].Name.GivenName, "scim-user-given-name-modified-0:"))
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list users with creation date filter",
|
||||
prepare: func(t require.TestingT) *scim.ListRequest {
|
||||
resp := createHumanUser(t, CTX, Instance.DefaultOrg.Id, 100)
|
||||
return &scim.ListRequest{
|
||||
Filter: gu.Ptr(fmt.Sprintf(`id eq "%s" and meta.created gt "%s"`, resp.UserId, testsInitializedUtc.Format(time.RFC3339))),
|
||||
}
|
||||
},
|
||||
assert: func(t assert.TestingT, resp *scim.ListResponse[*resources.ScimUser]) {
|
||||
assert.Len(t, resp.Resources, 1)
|
||||
assert.True(t, strings.HasPrefix(resp.Resources[0].UserName, "scim-username-100:"))
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "validate returned objects",
|
||||
req: &scim.ListRequest{
|
||||
Filter: gu.Ptr(fmt.Sprintf(`id eq "%s"`, createdUserIDs[0])),
|
||||
},
|
||||
assert: func(t assert.TestingT, resp *scim.ListResponse[*resources.ScimUser]) {
|
||||
assert.Len(t, resp.Resources, 1)
|
||||
if !test.PartiallyDeepEqual(fullUser, resp.Resources[0]) {
|
||||
t.Errorf("got = %#v, want %#v", resp.Resources[0], fullUser)
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "do not return user of other org",
|
||||
req: &scim.ListRequest{
|
||||
Filter: gu.Ptr(fmt.Sprintf(`id eq "%s"`, secondaryOrgCreatedUserIDs[0])),
|
||||
},
|
||||
assert: func(t assert.TestingT, resp *scim.ListResponse[*resources.ScimUser]) {
|
||||
assert.Len(t, resp.Resources, 0)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "do not count user of other org",
|
||||
prepare: func(t require.TestingT) *scim.ListRequest {
|
||||
iamOwnerCtx := Instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
|
||||
org := Instance.CreateOrganization(iamOwnerCtx, gofakeit.Name(), gofakeit.Email())
|
||||
resp := createHumanUser(t, iamOwnerCtx, org.OrganizationId, 102)
|
||||
|
||||
return &scim.ListRequest{
|
||||
{
|
||||
name: "count users with filter",
|
||||
req: &scim.ListRequest{
|
||||
Count: gu.Ptr(0),
|
||||
Filter: gu.Ptr(fmt.Sprintf(`id eq "%s"`, resp.UserId)),
|
||||
Filter: gu.Ptr(`emails sw "scim-email-1" and emails ew "@example.com"`),
|
||||
},
|
||||
assert: func(t assert.TestingT, resp *scim.ListResponse[*resources.ScimUser]) {
|
||||
assert.Equal(t, 0, resp.ItemsPerPage)
|
||||
assert.Equal(t, 2, resp.TotalResults)
|
||||
assert.Equal(t, 1, resp.StartIndex)
|
||||
assert.Len(t, resp.Resources, 0)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list users with modification date filter",
|
||||
prepare: func(t require.TestingT) *scim.ListRequest {
|
||||
userID := createdUserIDs[len(createdUserIDs)-1] // use the last entry, as we use the others for other assertions
|
||||
_, err := Instance.Client.UserV2.UpdateHumanUser(CTX, &user_v2.UpdateHumanUserRequest{
|
||||
UserId: userID,
|
||||
|
||||
Profile: &user_v2.SetHumanProfile{
|
||||
GivenName: "scim-user-given-name-modified-0: " + gofakeit.FirstName(),
|
||||
FamilyName: "scim-user-family-name-modified-0: " + gofakeit.LastName(),
|
||||
},
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
return &scim.ListRequest{
|
||||
// filter by id too to exclude other random users
|
||||
Filter: gu.Ptr(fmt.Sprintf(`id eq "%s" and meta.LASTMODIFIED gt "%s"`, userID, testsInitializedUtc.Format(time.RFC3339))),
|
||||
}
|
||||
},
|
||||
assert: func(t assert.TestingT, resp *scim.ListResponse[*resources.ScimUser]) {
|
||||
assert.Len(t, resp.Resources, 1)
|
||||
assert.Equal(t, resp.Resources[0].ID, createdUserIDs[len(createdUserIDs)-1])
|
||||
assert.True(t, strings.HasPrefix(resp.Resources[0].Name.FamilyName, "scim-user-family-name-modified-0:"))
|
||||
assert.True(t, strings.HasPrefix(resp.Resources[0].Name.GivenName, "scim-user-given-name-modified-0:"))
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list users with creation date filter",
|
||||
prepare: func(t require.TestingT) *scim.ListRequest {
|
||||
resp := createHumanUser(t, CTX, Instance.DefaultOrg.Id, 100)
|
||||
return &scim.ListRequest{
|
||||
Filter: gu.Ptr(fmt.Sprintf(`id eq "%s" and meta.created gt "%s"`, resp.UserId, testsInitializedUtc.Format(time.RFC3339))),
|
||||
}
|
||||
},
|
||||
assert: func(t assert.TestingT, resp *scim.ListResponse[*resources.ScimUser]) {
|
||||
assert.Len(t, resp.Resources, 1)
|
||||
assert.True(t, strings.HasPrefix(resp.Resources[0].UserName, "scim-username-100:"))
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "validate returned objects",
|
||||
req: &scim.ListRequest{
|
||||
Filter: gu.Ptr(fmt.Sprintf(`id eq "%s"`, createdUserIDs[0])),
|
||||
},
|
||||
assert: func(t assert.TestingT, resp *scim.ListResponse[*resources.ScimUser]) {
|
||||
assert.Len(t, resp.Resources, 1)
|
||||
if !test.PartiallyDeepEqual(fullUser, resp.Resources[0]) {
|
||||
t.Errorf("got = %#v, want %#v", resp.Resources[0], fullUser)
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "do not return user of other org",
|
||||
req: &scim.ListRequest{
|
||||
Filter: gu.Ptr(fmt.Sprintf(`id eq "%s"`, secondaryOrgCreatedUserIDs[0])),
|
||||
},
|
||||
assert: func(t assert.TestingT, resp *scim.ListResponse[*resources.ScimUser]) {
|
||||
assert.Len(t, resp.Resources, 0)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "do not count user of other org",
|
||||
prepare: func(t require.TestingT) *scim.ListRequest {
|
||||
iamOwnerCtx := Instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
|
||||
org := Instance.CreateOrganization(iamOwnerCtx, gofakeit.Name(), gofakeit.Email())
|
||||
resp := createHumanUser(t, iamOwnerCtx, org.OrganizationId, 102)
|
||||
|
||||
return &scim.ListRequest{
|
||||
Count: gu.Ptr(0),
|
||||
Filter: gu.Ptr(fmt.Sprintf(`id eq "%s"`, resp.UserId)),
|
||||
}
|
||||
},
|
||||
assert: func(t assert.TestingT, resp *scim.ListResponse[*resources.ScimUser]) {
|
||||
assert.Len(t, resp.Resources, 0)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "scoped externalID",
|
||||
prepare: func(t require.TestingT) *scim.ListRequest {
|
||||
resp := createHumanUser(t, CTX, Instance.DefaultOrg.Id, 102)
|
||||
|
||||
// set provisioning domain of service user
|
||||
setProvisioningDomain(t, Instance.Users.Get(integration.UserTypeOrgOwner).ID, "fooBar")
|
||||
|
||||
// set externalID for provisioning domain
|
||||
setAndEnsureMetadata(t, resp.UserId, "urn:zitadel:scim:fooBar:externalId", "100-scopedExternalId")
|
||||
return &scim.ListRequest{
|
||||
Filter: gu.Ptr(fmt.Sprintf(`id eq "%s"`, resp.UserId)),
|
||||
}
|
||||
},
|
||||
assert: func(t assert.TestingT, resp *scim.ListResponse[*resources.ScimUser]) {
|
||||
assert.Len(t, resp.Resources, 1)
|
||||
assert.Equal(t, resp.Resources[0].ExternalID, "100-scopedExternalId")
|
||||
},
|
||||
cleanup: func(t require.TestingT) {
|
||||
// delete provisioning domain of service user
|
||||
removeProvisioningDomain(t, Instance.Users.Get(integration.UserTypeOrgOwner).ID)
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.ctx == nil {
|
||||
tt.ctx = CTX
|
||||
}
|
||||
},
|
||||
assert: func(t assert.TestingT, resp *scim.ListResponse[*resources.ScimUser]) {
|
||||
assert.Len(t, resp.Resources, 0)
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "scoped externalID",
|
||||
prepare: func(t require.TestingT) *scim.ListRequest {
|
||||
resp := createHumanUser(t, CTX, Instance.DefaultOrg.Id, 102)
|
||||
|
||||
// set provisioning domain of service user
|
||||
setProvisioningDomain(t, Instance.Users.Get(integration.UserTypeOrgOwner).ID, "fooBar")
|
||||
|
||||
// set externalID for provisioning domain
|
||||
setAndEnsureMetadata(t, resp.UserId, "urn:zitadel:scim:fooBar:externalId", "100-scopedExternalId")
|
||||
return &scim.ListRequest{
|
||||
Filter: gu.Ptr(fmt.Sprintf(`id eq "%s"`, resp.UserId)),
|
||||
if tt.prepare != nil {
|
||||
tt.req = tt.prepare(t)
|
||||
}
|
||||
},
|
||||
assert: func(t assert.TestingT, resp *scim.ListResponse[*resources.ScimUser]) {
|
||||
assert.Len(t, resp.Resources, 1)
|
||||
assert.Equal(t, resp.Resources[0].ExternalID, "100-scopedExternalId")
|
||||
},
|
||||
cleanup: func(t require.TestingT) {
|
||||
// delete provisioning domain of service user
|
||||
removeProvisioningDomain(t, Instance.Users.Get(integration.UserTypeOrgOwner).ID)
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.ctx == nil {
|
||||
tt.ctx = CTX
|
||||
}
|
||||
|
||||
if tt.prepare != nil {
|
||||
tt.req = tt.prepare(t)
|
||||
}
|
||||
if tt.orgID == "" {
|
||||
tt.orgID = Instance.DefaultOrg.Id
|
||||
}
|
||||
|
||||
if tt.orgID == "" {
|
||||
tt.orgID = Instance.DefaultOrg.Id
|
||||
}
|
||||
retryDuration, tick := integration.WaitForAndTickWithMaxDuration(tt.ctx, time.Minute)
|
||||
require.EventuallyWithT(t, func(ttt *assert.CollectT) {
|
||||
listResp, err := Instance.Client.SCIM.Users.List(tt.ctx, tt.orgID, tt.req)
|
||||
if tt.wantErr {
|
||||
statusCode := tt.errorStatus
|
||||
if statusCode == 0 {
|
||||
statusCode = http.StatusBadRequest
|
||||
}
|
||||
|
||||
retryDuration, tick := integration.WaitForAndTickWithMaxDuration(tt.ctx, time.Minute)
|
||||
require.EventuallyWithT(t, func(ttt *assert.CollectT) {
|
||||
listResp, err := Instance.Client.SCIM.Users.List(tt.ctx, tt.orgID, tt.req)
|
||||
if tt.wantErr {
|
||||
statusCode := tt.errorStatus
|
||||
if statusCode == 0 {
|
||||
statusCode = http.StatusBadRequest
|
||||
scimErr := scim.RequireScimError(ttt, statusCode, err)
|
||||
if tt.errorType != "" {
|
||||
assert.Equal(t, tt.errorType, scimErr.Error.ScimType)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
scimErr := scim.RequireScimError(ttt, statusCode, err)
|
||||
if tt.errorType != "" {
|
||||
assert.Equal(t, tt.errorType, scimErr.Error.ScimType)
|
||||
require.NoError(t, err)
|
||||
assert.EqualValues(ttt, []schemas.ScimSchemaType{"urn:ietf:params:scim:api:messages:2.0:ListResponse"}, listResp.Schemas)
|
||||
if tt.assert != nil {
|
||||
tt.assert(ttt, listResp)
|
||||
}
|
||||
return
|
||||
}
|
||||
}, retryDuration, tick)
|
||||
|
||||
require.NoError(t, err)
|
||||
assert.EqualValues(ttt, []schemas.ScimSchemaType{"urn:ietf:params:scim:api:messages:2.0:ListResponse"}, listResp.Schemas)
|
||||
if tt.assert != nil {
|
||||
tt.assert(ttt, listResp)
|
||||
if tt.cleanup != nil {
|
||||
tt.cleanup(t)
|
||||
}
|
||||
}, retryDuration, tick)
|
||||
|
||||
if tt.cleanup != nil {
|
||||
tt.cleanup(t)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
func sortScimUserByUsername(users []*resources.ScimUser) []*resources.ScimUser {
|
||||
sortedResources := users
|
||||
slices.SortFunc(sortedResources, func(a, b *resources.ScimUser) int {
|
||||
|
Reference in New Issue
Block a user