mirror of
https://github.com/zitadel/zitadel.git
synced 2025-12-24 03:16:48 +00:00
fix: Force v2 permission checks on user listing
# Which Problems Are Solved When the feature flag for enabling permission checks v2 is disabled, a user without permission could list users across instances and get the total number of users available. # How the Problems Are Solved Disregard the state of the feature flag and always enforce permission checks v2 on v2 APIs. --------- Co-authored-by: Livio Spring <livio.a@gmail.com> (cherry picked from commit826039c620) (cherry picked from commit0e17d0005a)
This commit is contained in:
@@ -4,7 +4,6 @@ package user_test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"slices"
|
"slices"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -17,61 +16,11 @@ import (
|
|||||||
"google.golang.org/protobuf/types/known/timestamppb"
|
"google.golang.org/protobuf/types/known/timestamppb"
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/internal/integration"
|
"github.com/zitadel/zitadel/internal/integration"
|
||||||
"github.com/zitadel/zitadel/pkg/grpc/feature/v2"
|
|
||||||
"github.com/zitadel/zitadel/pkg/grpc/object/v2"
|
"github.com/zitadel/zitadel/pkg/grpc/object/v2"
|
||||||
"github.com/zitadel/zitadel/pkg/grpc/session/v2"
|
"github.com/zitadel/zitadel/pkg/grpc/session/v2"
|
||||||
"github.com/zitadel/zitadel/pkg/grpc/user/v2"
|
"github.com/zitadel/zitadel/pkg/grpc/user/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
|
||||||
permissionCheckV2SetFlagInital bool
|
|
||||||
permissionCheckV2SetFlag bool
|
|
||||||
)
|
|
||||||
|
|
||||||
type permissionCheckV2SettingsStruct struct {
|
|
||||||
TestNamePrependString string
|
|
||||||
SetFlag bool
|
|
||||||
}
|
|
||||||
|
|
||||||
var permissionCheckV2Settings []permissionCheckV2SettingsStruct = []permissionCheckV2SettingsStruct{
|
|
||||||
{
|
|
||||||
SetFlag: false,
|
|
||||||
TestNamePrependString: "permission_check_v2 IS NOT SET" + " ",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
SetFlag: true,
|
|
||||||
TestNamePrependString: "permission_check_v2 IS SET" + " ",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func setPermissionCheckV2Flag(t *testing.T, setFlag bool) {
|
|
||||||
if permissionCheckV2SetFlagInital && permissionCheckV2SetFlag == setFlag {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err := Instance.Client.FeatureV2.SetInstanceFeatures(IamCTX, &feature.SetInstanceFeaturesRequest{
|
|
||||||
PermissionCheckV2: &setFlag,
|
|
||||||
})
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
var flagSet bool
|
|
||||||
for i := 0; !flagSet || i < 6; i++ {
|
|
||||||
res, err := Instance.Client.FeatureV2.GetInstanceFeatures(IamCTX, &feature.GetInstanceFeaturesRequest{})
|
|
||||||
require.NoError(t, err)
|
|
||||||
if res.PermissionCheckV2.Enabled == setFlag {
|
|
||||||
flagSet = true
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
time.Sleep(10 * time.Second)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !flagSet {
|
|
||||||
require.NoError(t, errors.New("unable to set permission_check_v2 flag"))
|
|
||||||
}
|
|
||||||
permissionCheckV2SetFlagInital = true
|
|
||||||
permissionCheckV2SetFlag = setFlag
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestServer_GetUserByID(t *testing.T) {
|
func TestServer_GetUserByID(t *testing.T) {
|
||||||
orgResp := Instance.CreateOrganization(IamCTX, fmt.Sprintf("GetUserByIDOrg-%s", gofakeit.AppName()), gofakeit.Email())
|
orgResp := Instance.CreateOrganization(IamCTX, fmt.Sprintf("GetUserByIDOrg-%s", gofakeit.AppName()), gofakeit.Email())
|
||||||
type args struct {
|
type args struct {
|
||||||
@@ -433,11 +382,6 @@ func createUserWithUserName(ctx context.Context, username string, orgID string,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestServer_ListUsers(t *testing.T) {
|
func TestServer_ListUsers(t *testing.T) {
|
||||||
defer func() {
|
|
||||||
_, err := Instance.Client.FeatureV2.ResetInstanceFeatures(IamCTX, &feature.ResetInstanceFeaturesRequest{})
|
|
||||||
require.NoError(t, err)
|
|
||||||
}()
|
|
||||||
|
|
||||||
orgResp := Instance.CreateOrganization(IamCTX, fmt.Sprintf("ListUsersOrg-%s", gofakeit.AppName()), gofakeit.Email())
|
orgResp := Instance.CreateOrganization(IamCTX, fmt.Sprintf("ListUsersOrg-%s", gofakeit.AppName()), gofakeit.Email())
|
||||||
type args struct {
|
type args struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
@@ -1119,7 +1063,7 @@ func TestServer_ListUsers(t *testing.T) {
|
|||||||
},
|
},
|
||||||
want: &user.ListUsersResponse{
|
want: &user.ListUsersResponse{
|
||||||
Details: &object.ListDetails{
|
Details: &object.ListDetails{
|
||||||
TotalResult: 0,
|
TotalResult: 1,
|
||||||
Timestamp: timestamppb.Now(),
|
Timestamp: timestamppb.Now(),
|
||||||
},
|
},
|
||||||
SortingColumn: 0,
|
SortingColumn: 0,
|
||||||
@@ -1130,65 +1074,54 @@ func TestServer_ListUsers(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, f := range permissionCheckV2Settings {
|
for _, tt := range tests {
|
||||||
f := f
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
for _, tt := range tests {
|
infos := tt.args.dep(IamCTX, tt.args.req)
|
||||||
t.Run(f.TestNamePrependString+tt.name, func(t *testing.T) {
|
|
||||||
setPermissionCheckV2Flag(t, f.SetFlag)
|
|
||||||
infos := tt.args.dep(IamCTX, tt.args.req)
|
|
||||||
|
|
||||||
retryDuration, tick := integration.WaitForAndTickWithMaxDuration(tt.args.ctx, 10*time.Minute)
|
retryDuration, tick := integration.WaitForAndTickWithMaxDuration(tt.args.ctx, 10*time.Minute)
|
||||||
require.EventuallyWithT(t, func(ttt *assert.CollectT) {
|
require.EventuallyWithT(t, func(ttt *assert.CollectT) {
|
||||||
got, err := Client.ListUsers(tt.args.ctx, tt.args.req)
|
got, err := Client.ListUsers(tt.args.ctx, tt.args.req)
|
||||||
if tt.wantErr {
|
if tt.wantErr {
|
||||||
require.Error(ttt, err)
|
require.Error(ttt, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
require.NoError(ttt, err)
|
require.NoError(ttt, err)
|
||||||
|
|
||||||
// always only give back dependency infos which are required for the response
|
// always only give back dependency infos which are required for the response
|
||||||
require.Len(ttt, tt.want.Result, len(infos))
|
require.Len(ttt, tt.want.Result, len(infos))
|
||||||
if assert.Len(ttt, got.Result, len(tt.want.Result)) {
|
if assert.Len(ttt, got.Result, len(tt.want.Result)) {
|
||||||
tt.want.Details.TotalResult = got.Details.TotalResult
|
// fill in userid and username as it is generated
|
||||||
|
for i := range infos {
|
||||||
// fill in userid and username as it is generated
|
if tt.want.Result[i] == nil {
|
||||||
for i := range infos {
|
continue
|
||||||
if tt.want.Result[i] == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
tt.want.Result[i].UserId = infos[i].UserID
|
|
||||||
tt.want.Result[i].Username = infos[i].Username
|
|
||||||
tt.want.Result[i].PreferredLoginName = infos[i].Username
|
|
||||||
tt.want.Result[i].LoginNames = []string{infos[i].Username}
|
|
||||||
if human := tt.want.Result[i].GetHuman(); human != nil {
|
|
||||||
human.Email.Email = infos[i].Username
|
|
||||||
human.Phone.Phone = infos[i].Phone
|
|
||||||
if tt.want.Result[i].GetHuman().GetPasswordChanged() != nil {
|
|
||||||
human.PasswordChanged = infos[i].Changed
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tt.want.Result[i].Details = infos[i].Details
|
|
||||||
}
|
}
|
||||||
for i := range tt.want.Result {
|
tt.want.Result[i].UserId = infos[i].UserID
|
||||||
if tt.want.Result[i] == nil {
|
tt.want.Result[i].Username = infos[i].Username
|
||||||
continue
|
tt.want.Result[i].PreferredLoginName = infos[i].Username
|
||||||
|
tt.want.Result[i].LoginNames = []string{infos[i].Username}
|
||||||
|
if human := tt.want.Result[i].GetHuman(); human != nil {
|
||||||
|
human.Email.Email = infos[i].Username
|
||||||
|
human.Phone.Phone = infos[i].Phone
|
||||||
|
if tt.want.Result[i].GetHuman().GetPasswordChanged() != nil {
|
||||||
|
human.PasswordChanged = infos[i].Changed
|
||||||
}
|
}
|
||||||
assert.EqualExportedValues(ttt, got.Result[i], tt.want.Result[i])
|
|
||||||
}
|
}
|
||||||
|
tt.want.Result[i].Details = infos[i].Details
|
||||||
}
|
}
|
||||||
integration.AssertListDetails(ttt, tt.want, got)
|
for i := range tt.want.Result {
|
||||||
}, retryDuration, tick, "timeout waiting for expected user result")
|
if tt.want.Result[i] == nil {
|
||||||
})
|
continue
|
||||||
}
|
}
|
||||||
|
assert.EqualExportedValues(ttt, got.Result[i], tt.want.Result[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
integration.AssertListDetails(ttt, tt.want, got)
|
||||||
|
}, retryDuration, tick, "timeout waiting for expected user result")
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestServer_SystemUsers_ListUsers(t *testing.T) {
|
func TestServer_SystemUsers_ListUsers(t *testing.T) {
|
||||||
defer func() {
|
|
||||||
_, err := Instance.Client.FeatureV2.ResetInstanceFeatures(IamCTX, &feature.ResetInstanceFeaturesRequest{})
|
|
||||||
require.NoError(t, err)
|
|
||||||
}()
|
|
||||||
|
|
||||||
org1 := Instance.CreateOrganization(IamCTX, fmt.Sprintf("ListUsersOrg-%s", gofakeit.AppName()), gofakeit.Email())
|
org1 := Instance.CreateOrganization(IamCTX, fmt.Sprintf("ListUsersOrg-%s", gofakeit.AppName()), gofakeit.Email())
|
||||||
org2 := Instance.CreateOrganization(IamCTX, fmt.Sprintf("ListUsersOrg-%s", gofakeit.AppName()), "org2@zitadel.com")
|
org2 := Instance.CreateOrganization(IamCTX, fmt.Sprintf("ListUsersOrg-%s", gofakeit.AppName()), "org2@zitadel.com")
|
||||||
org3 := Instance.CreateOrganization(IamCTX, fmt.Sprintf("ListUsersOrg-%s", gofakeit.AppName()), gofakeit.Email())
|
org3 := Instance.CreateOrganization(IamCTX, fmt.Sprintf("ListUsersOrg-%s", gofakeit.AppName()), gofakeit.Email())
|
||||||
@@ -1239,38 +1172,33 @@ func TestServer_SystemUsers_ListUsers(t *testing.T) {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, f := range permissionCheckV2Settings {
|
for _, tt := range tests {
|
||||||
f := f
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
for _, tt := range tests {
|
retryDuration, tick := integration.WaitForAndTickWithMaxDuration(tt.ctx, 1*time.Minute)
|
||||||
t.Run(f.TestNamePrependString+tt.name, func(t *testing.T) {
|
require.EventuallyWithT(t, func(ttt *assert.CollectT) {
|
||||||
setPermissionCheckV2Flag(t, f.SetFlag)
|
got, err := Client.ListUsers(tt.ctx, tt.req)
|
||||||
|
require.NoError(ttt, err)
|
||||||
|
|
||||||
retryDuration, tick := integration.WaitForAndTickWithMaxDuration(tt.ctx, 1*time.Minute)
|
if tt.checkNumberOfUsersReturned {
|
||||||
require.EventuallyWithT(t, func(ttt *assert.CollectT) {
|
require.Equal(t, len(tt.expectedFoundUsernames), len(got.Result))
|
||||||
got, err := Client.ListUsers(tt.ctx, tt.req)
|
}
|
||||||
require.NoError(ttt, err)
|
|
||||||
|
|
||||||
if tt.checkNumberOfUsersReturned {
|
if tt.expectedFoundUsernames != nil {
|
||||||
require.Equal(t, len(tt.expectedFoundUsernames), len(got.Result))
|
for _, user := range got.Result {
|
||||||
}
|
for i, username := range tt.expectedFoundUsernames {
|
||||||
|
if username == user.Username {
|
||||||
if tt.expectedFoundUsernames != nil {
|
tt.expectedFoundUsernames = tt.expectedFoundUsernames[i+1:]
|
||||||
for _, user := range got.Result {
|
break
|
||||||
for i, username := range tt.expectedFoundUsernames {
|
|
||||||
if username == user.Username {
|
|
||||||
tt.expectedFoundUsernames = tt.expectedFoundUsernames[i+1:]
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(tt.expectedFoundUsernames) == 0 {
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
require.FailNow(t, "unable to find all users with specified usernames")
|
if len(tt.expectedFoundUsernames) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, retryDuration, tick, "timeout waiting for expected user result")
|
require.FailNow(t, "unable to find all users with specified usernames")
|
||||||
})
|
}
|
||||||
}
|
}, retryDuration, tick, "timeout waiting for expected user result")
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ package user_test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"slices"
|
"slices"
|
||||||
"testing"
|
"testing"
|
||||||
@@ -17,7 +16,6 @@ import (
|
|||||||
"google.golang.org/protobuf/types/known/timestamppb"
|
"google.golang.org/protobuf/types/known/timestamppb"
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/internal/integration"
|
"github.com/zitadel/zitadel/internal/integration"
|
||||||
"github.com/zitadel/zitadel/pkg/grpc/feature/v2"
|
|
||||||
"github.com/zitadel/zitadel/pkg/grpc/object/v2"
|
"github.com/zitadel/zitadel/pkg/grpc/object/v2"
|
||||||
object_v2beta "github.com/zitadel/zitadel/pkg/grpc/object/v2beta"
|
object_v2beta "github.com/zitadel/zitadel/pkg/grpc/object/v2beta"
|
||||||
"github.com/zitadel/zitadel/pkg/grpc/session/v2"
|
"github.com/zitadel/zitadel/pkg/grpc/session/v2"
|
||||||
@@ -32,55 +30,6 @@ func detailsV2ToV2beta(obj *object.Details) *object_v2beta.Details {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
|
||||||
permissionCheckV2SetFlagInital bool
|
|
||||||
permissionCheckV2SetFlag bool
|
|
||||||
)
|
|
||||||
|
|
||||||
type permissionCheckV2SettingsStruct struct {
|
|
||||||
TestNamePrependString string
|
|
||||||
SetFlag bool
|
|
||||||
}
|
|
||||||
|
|
||||||
var permissionCheckV2Settings []permissionCheckV2SettingsStruct = []permissionCheckV2SettingsStruct{
|
|
||||||
{
|
|
||||||
SetFlag: false,
|
|
||||||
TestNamePrependString: "permission_check_v2 IS NOT SET" + " ",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
SetFlag: true,
|
|
||||||
TestNamePrependString: "permission_check_v2 IS SET" + " ",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
func setPermissionCheckV2Flag(t *testing.T, setFlag bool) {
|
|
||||||
if permissionCheckV2SetFlagInital && permissionCheckV2SetFlag == setFlag {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err := Instance.Client.FeatureV2.SetInstanceFeatures(IamCTX, &feature.SetInstanceFeaturesRequest{
|
|
||||||
PermissionCheckV2: &setFlag,
|
|
||||||
})
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
var flagSet bool
|
|
||||||
for i := 0; !flagSet || i < 6; i++ {
|
|
||||||
res, err := Instance.Client.FeatureV2.GetInstanceFeatures(IamCTX, &feature.GetInstanceFeaturesRequest{})
|
|
||||||
require.NoError(t, err)
|
|
||||||
if res.PermissionCheckV2.Enabled == setFlag {
|
|
||||||
flagSet = true
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
time.Sleep(10 * time.Second)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !flagSet {
|
|
||||||
require.NoError(t, errors.New("unable to set permission_check_v2 flag"))
|
|
||||||
}
|
|
||||||
permissionCheckV2SetFlagInital = true
|
|
||||||
permissionCheckV2SetFlag = setFlag
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestServer_GetUserByID(t *testing.T) {
|
func TestServer_GetUserByID(t *testing.T) {
|
||||||
orgResp := Instance.CreateOrganization(IamCTX, fmt.Sprintf("GetUserByIDOrg-%s", gofakeit.AppName()), gofakeit.Email())
|
orgResp := Instance.CreateOrganization(IamCTX, fmt.Sprintf("GetUserByIDOrg-%s", gofakeit.AppName()), gofakeit.Email())
|
||||||
type args struct {
|
type args struct {
|
||||||
@@ -433,11 +382,6 @@ func createUser(ctx context.Context, orgID string, passwordChangeRequired bool)
|
|||||||
}
|
}
|
||||||
|
|
||||||
func TestServer_ListUsers(t *testing.T) {
|
func TestServer_ListUsers(t *testing.T) {
|
||||||
defer func() {
|
|
||||||
_, err := Instance.Client.FeatureV2.ResetInstanceFeatures(IamCTX, &feature.ResetInstanceFeaturesRequest{})
|
|
||||||
require.NoError(t, err)
|
|
||||||
}()
|
|
||||||
|
|
||||||
orgResp := Instance.CreateOrganization(IamCTX, fmt.Sprintf("ListUsersOrg-%s", gofakeit.AppName()), gofakeit.Email())
|
orgResp := Instance.CreateOrganization(IamCTX, fmt.Sprintf("ListUsersOrg-%s", gofakeit.AppName()), gofakeit.Email())
|
||||||
type args struct {
|
type args struct {
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
@@ -1122,7 +1066,7 @@ func TestServer_ListUsers(t *testing.T) {
|
|||||||
},
|
},
|
||||||
want: &user.ListUsersResponse{
|
want: &user.ListUsersResponse{
|
||||||
Details: &object_v2beta.ListDetails{
|
Details: &object_v2beta.ListDetails{
|
||||||
TotalResult: 0,
|
TotalResult: 1,
|
||||||
Timestamp: timestamppb.Now(),
|
Timestamp: timestamppb.Now(),
|
||||||
},
|
},
|
||||||
SortingColumn: 0,
|
SortingColumn: 0,
|
||||||
@@ -1133,59 +1077,52 @@ func TestServer_ListUsers(t *testing.T) {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, f := range permissionCheckV2Settings {
|
for _, tt := range tests {
|
||||||
f := f
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
for _, tt := range tests {
|
infos := tt.args.dep(IamCTX, tt.args.req)
|
||||||
t.Run(f.TestNamePrependString+tt.name, func(t *testing.T) {
|
|
||||||
setPermissionCheckV2Flag(t, f.SetFlag)
|
|
||||||
infos := tt.args.dep(IamCTX, tt.args.req)
|
|
||||||
|
|
||||||
// retryDuration, tick := integration.WaitForAndTickWithMaxDuration(tt.args.ctx, 10*time.Minute)
|
// retryDuration, tick := integration.WaitForAndTickWithMaxDuration(tt.args.ctx, 10*time.Minute)
|
||||||
retryDuration, tick := integration.WaitForAndTickWithMaxDuration(tt.args.ctx, 20*time.Second)
|
retryDuration, tick := integration.WaitForAndTickWithMaxDuration(tt.args.ctx, 20*time.Second)
|
||||||
require.EventuallyWithT(t, func(ttt *assert.CollectT) {
|
require.EventuallyWithT(t, func(ttt *assert.CollectT) {
|
||||||
got, err := Client.ListUsers(tt.args.ctx, tt.args.req)
|
got, err := Client.ListUsers(tt.args.ctx, tt.args.req)
|
||||||
if tt.wantErr {
|
if tt.wantErr {
|
||||||
require.Error(ttt, err)
|
require.Error(ttt, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
require.NoError(ttt, err)
|
require.NoError(ttt, err)
|
||||||
|
|
||||||
// always only give back dependency infos which are required for the response
|
// always only give back dependency infos which are required for the response
|
||||||
require.Len(ttt, tt.want.Result, len(infos))
|
require.Len(ttt, tt.want.Result, len(infos))
|
||||||
// always first check length, otherwise its failed anyway
|
// always first check length, otherwise its failed anyway
|
||||||
if assert.Len(ttt, got.Result, len(tt.want.Result)) {
|
if assert.Len(ttt, got.Result, len(tt.want.Result)) {
|
||||||
// totalResult is unrelated to the tests here so gets carried over, can vary from the count of results due to permissions
|
// fill in userid and username as it is generated
|
||||||
tt.want.Details.TotalResult = got.Details.TotalResult
|
for i := range infos {
|
||||||
|
if tt.want.Result[i] == nil {
|
||||||
// fill in userid and username as it is generated
|
continue
|
||||||
for i := range infos {
|
|
||||||
if tt.want.Result[i] == nil {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
tt.want.Result[i].UserId = infos[i].UserID
|
|
||||||
tt.want.Result[i].Username = infos[i].Username
|
|
||||||
tt.want.Result[i].PreferredLoginName = infos[i].Username
|
|
||||||
tt.want.Result[i].LoginNames = []string{infos[i].Username}
|
|
||||||
if human := tt.want.Result[i].GetHuman(); human != nil {
|
|
||||||
human.Email.Email = infos[i].Username
|
|
||||||
human.Phone.Phone = infos[i].Phone
|
|
||||||
if tt.want.Result[i].GetHuman().GetPasswordChanged() != nil {
|
|
||||||
human.PasswordChanged = infos[i].Changed
|
|
||||||
}
|
|
||||||
}
|
|
||||||
tt.want.Result[i].Details = detailsV2ToV2beta(infos[i].Details)
|
|
||||||
}
|
}
|
||||||
for i := range tt.want.Result {
|
tt.want.Result[i].UserId = infos[i].UserID
|
||||||
if tt.want.Result[i] == nil {
|
tt.want.Result[i].Username = infos[i].Username
|
||||||
continue
|
tt.want.Result[i].PreferredLoginName = infos[i].Username
|
||||||
|
tt.want.Result[i].LoginNames = []string{infos[i].Username}
|
||||||
|
if human := tt.want.Result[i].GetHuman(); human != nil {
|
||||||
|
human.Email.Email = infos[i].Username
|
||||||
|
human.Phone.Phone = infos[i].Phone
|
||||||
|
if tt.want.Result[i].GetHuman().GetPasswordChanged() != nil {
|
||||||
|
human.PasswordChanged = infos[i].Changed
|
||||||
}
|
}
|
||||||
assert.EqualExportedValues(ttt, got.Result[i], tt.want.Result[i])
|
|
||||||
}
|
}
|
||||||
|
tt.want.Result[i].Details = detailsV2ToV2beta(infos[i].Details)
|
||||||
}
|
}
|
||||||
integration.AssertListDetails(ttt, tt.want, got)
|
for i := range tt.want.Result {
|
||||||
}, retryDuration, tick, "timeout waiting for expected user result")
|
if tt.want.Result[i] == nil {
|
||||||
})
|
continue
|
||||||
}
|
}
|
||||||
|
assert.EqualExportedValues(ttt, got.Result[i], tt.want.Result[i])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
integration.AssertListDetails(ttt, tt.want, got)
|
||||||
|
}, retryDuration, tick, "timeout waiting for expected user result")
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import (
|
|||||||
"database/sql"
|
"database/sql"
|
||||||
_ "embed"
|
_ "embed"
|
||||||
"errors"
|
"errors"
|
||||||
"slices"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -124,14 +123,6 @@ type NotifyUser struct {
|
|||||||
PasswordSet bool
|
PasswordSet bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func usersCheckPermission(ctx context.Context, users *Users, permissionCheck domain.PermissionCheck) {
|
|
||||||
users.Users = slices.DeleteFunc(users.Users,
|
|
||||||
func(user *User) bool {
|
|
||||||
return userCheckPermission(ctx, user.ResourceOwner, user.ID, permissionCheck) != nil
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
type UserSearchQueries struct {
|
type UserSearchQueries struct {
|
||||||
SearchRequest
|
SearchRequest
|
||||||
Queries []SearchQuery
|
Queries []SearchQuery
|
||||||
@@ -601,13 +592,11 @@ func (q *Queries) CountUsers(ctx context.Context, queries *UserSearchQueries) (c
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (q *Queries) SearchUsers(ctx context.Context, queries *UserSearchQueries, filterOrgIds string, permissionCheck domain.PermissionCheck) (*Users, error) {
|
func (q *Queries) SearchUsers(ctx context.Context, queries *UserSearchQueries, filterOrgIds string, permissionCheck domain.PermissionCheck) (*Users, error) {
|
||||||
users, err := q.searchUsers(ctx, queries, filterOrgIds, permissionCheck != nil && authz.GetFeatures(ctx).PermissionCheckV2)
|
users, err := q.searchUsers(ctx, queries, filterOrgIds, permissionCheck != nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if permissionCheck != nil && !authz.GetFeatures(ctx).PermissionCheckV2 {
|
|
||||||
usersCheckPermission(ctx, users, permissionCheck)
|
|
||||||
}
|
|
||||||
return users, nil
|
return users, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -10,7 +10,6 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
|
||||||
"golang.org/x/text/language"
|
"golang.org/x/text/language"
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/internal/api/authz"
|
"github.com/zitadel/zitadel/internal/api/authz"
|
||||||
@@ -19,129 +18,6 @@ import (
|
|||||||
"github.com/zitadel/zitadel/internal/zerrors"
|
"github.com/zitadel/zitadel/internal/zerrors"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestUser_usersCheckPermission(t *testing.T) {
|
|
||||||
type want struct {
|
|
||||||
users []*User
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
want want
|
|
||||||
users *Users
|
|
||||||
permissions []string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
"permissions for all users",
|
|
||||||
want{
|
|
||||||
users: []*User{
|
|
||||||
{ID: "first"}, {ID: "second"}, {ID: "third"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
&Users{
|
|
||||||
Users: []*User{
|
|
||||||
{ID: "first"}, {ID: "second"}, {ID: "third"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
[]string{"first", "second", "third"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"permissions for one user, first",
|
|
||||||
want{
|
|
||||||
users: []*User{
|
|
||||||
{ID: "first"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
&Users{
|
|
||||||
Users: []*User{
|
|
||||||
{ID: "first"}, {ID: "second"}, {ID: "third"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
[]string{"first"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"permissions for one user, second",
|
|
||||||
want{
|
|
||||||
users: []*User{
|
|
||||||
{ID: "second"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
&Users{
|
|
||||||
Users: []*User{
|
|
||||||
{ID: "first"}, {ID: "second"}, {ID: "third"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
[]string{"second"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"permissions for one user, third",
|
|
||||||
want{
|
|
||||||
users: []*User{
|
|
||||||
{ID: "third"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
&Users{
|
|
||||||
Users: []*User{
|
|
||||||
{ID: "first"}, {ID: "second"}, {ID: "third"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
[]string{"third"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"permissions for two users, first",
|
|
||||||
want{
|
|
||||||
users: []*User{
|
|
||||||
{ID: "first"}, {ID: "third"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
&Users{
|
|
||||||
Users: []*User{
|
|
||||||
{ID: "first"}, {ID: "second"}, {ID: "third"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
[]string{"first", "third"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"permissions for two users, second",
|
|
||||||
want{
|
|
||||||
users: []*User{
|
|
||||||
{ID: "second"}, {ID: "third"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
&Users{
|
|
||||||
Users: []*User{
|
|
||||||
{ID: "first"}, {ID: "second"}, {ID: "third"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
[]string{"second", "third"},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"no permissions",
|
|
||||||
want{
|
|
||||||
users: []*User{},
|
|
||||||
},
|
|
||||||
&Users{
|
|
||||||
Users: []*User{
|
|
||||||
{ID: "first"}, {ID: "second"}, {ID: "third"},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
[]string{},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
checkPermission := func(ctx context.Context, permission, orgID, resourceID string) (err error) {
|
|
||||||
for _, perm := range tt.permissions {
|
|
||||||
if resourceID == perm {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return errors.New("failed")
|
|
||||||
}
|
|
||||||
usersCheckPermission(context.Background(), tt.users, checkPermission)
|
|
||||||
require.Equal(t, tt.want.users, tt.users.Users)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func TestUser_userCheckPermission(t *testing.T) {
|
func TestUser_userCheckPermission(t *testing.T) {
|
||||||
type args struct {
|
type args struct {
|
||||||
ctxData string
|
ctxData string
|
||||||
|
|||||||
Reference in New Issue
Block a user