diff --git a/internal/api/grpc/instance/v2/integration_test/domain_test.go b/internal/api/grpc/instance/v2/integration_test/domain_test.go index 110ef5123..2c8c48cd4 100644 --- a/internal/api/grpc/instance/v2/integration_test/domain_test.go +++ b/internal/api/grpc/instance/v2/integration_test/domain_test.go @@ -4,19 +4,16 @@ package instance_test import ( "context" - "fmt" "strings" "testing" "time" - "github.com/muhlemmer/gu" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "github.com/zitadel/zitadel/internal/integration" - "github.com/zitadel/zitadel/pkg/grpc/feature/v2" "github.com/zitadel/zitadel/pkg/grpc/instance/v2" ) @@ -32,98 +29,80 @@ func TestAddCustomDomain(t *testing.T) { inst := integration.NewInstance(ctxWithSysAuthZ) iamOwnerCtx := inst.WithAuthorizationToken(context.Background(), integration.UserTypeIAMOwner) - relationalInst := integration.NewInstance(ctxWithSysAuthZ) - iamOwnerRelationalCtx := relationalInst.WithAuthorizationToken(context.Background(), integration.UserTypeIAMOwner) - integration.EnsureInstanceFeature(t, ctxWithSysAuthZ, relationalInst, &feature.SetInstanceFeaturesRequest{EnableRelationalTables: gu.Ptr(true)}, func(tCollect *assert.CollectT, got *feature.GetInstanceFeaturesResponse) { - assert.True(tCollect, got.EnableRelationalTables.GetEnabled()) - }) - t.Cleanup(func() { inst.Client.InstanceV2.DeleteInstance(ctxWithSysAuthZ, &instance.DeleteInstanceRequest{InstanceId: inst.ID()}) - relationalInst.Client.InstanceV2.DeleteInstance(ctxWithSysAuthZ, &instance.DeleteInstanceRequest{InstanceId: relationalInst.ID()}) }) // ugly fix to wait until instances are projected in all read models. time.Sleep(10 * time.Second) - type instanceAndCtx struct { - testType string - instance *integration.Instance - ctx context.Context + tt := []struct { + testName string + inputContext context.Context + inputRequest *instance.AddCustomDomainRequest + expectedErrorMsg string + expectedErrorCode codes.Code + }{ + { + testName: "when invalid context should return unauthN error", + inputRequest: &instance.AddCustomDomainRequest{ + InstanceId: inst.ID(), + CustomDomain: integration.DomainName(), + }, + inputContext: context.Background(), + expectedErrorCode: codes.Unauthenticated, + expectedErrorMsg: "auth header missing", + }, + { + testName: "when unauthZ context should return unauthZ error", + inputRequest: &instance.AddCustomDomainRequest{ + InstanceId: inst.ID(), + CustomDomain: integration.DomainName(), + }, + inputContext: iamOwnerCtx, + expectedErrorCode: codes.PermissionDenied, + expectedErrorMsg: "No matching permissions found (AUTH-5mWD2)", + }, + { + testName: "when invalid domain should return invalid argument error", + inputRequest: &instance.AddCustomDomainRequest{ + InstanceId: inst.ID(), + CustomDomain: " ", + }, + inputContext: ctxWithSysAuthZ, + expectedErrorCode: codes.InvalidArgument, + expectedErrorMsg: "Errors.Invalid.Argument", + }, + { + testName: "when valid request should return successful response", + inputRequest: &instance.AddCustomDomainRequest{ + InstanceId: inst.ID(), + CustomDomain: " " + integration.DomainName(), + }, + inputContext: ctxWithSysAuthZ, + }, } - testedInstances := []instanceAndCtx{ - {testType: "eventstore", instance: inst, ctx: iamOwnerCtx}, - {testType: "relational", instance: relationalInst, ctx: iamOwnerRelationalCtx}, - } - for _, instAndCtx := range testedInstances { - tt := []struct { - testName string - inputContext context.Context - inputRequest *instance.AddCustomDomainRequest - expectedErrorMsg string - expectedErrorCode codes.Code - }{ - { - testName: "when invalid context should return unauthN error", - inputRequest: &instance.AddCustomDomainRequest{ - InstanceId: instAndCtx.instance.ID(), - CustomDomain: integration.DomainName(), - }, - inputContext: context.Background(), - expectedErrorCode: codes.Unauthenticated, - expectedErrorMsg: "auth header missing", - }, - { - testName: "when unauthZ context should return unauthZ error", - inputRequest: &instance.AddCustomDomainRequest{ - InstanceId: instAndCtx.instance.ID(), - CustomDomain: integration.DomainName(), - }, - inputContext: instAndCtx.ctx, - expectedErrorCode: codes.PermissionDenied, - expectedErrorMsg: "No matching permissions found (AUTH-5mWD2)", - }, - { - testName: "when invalid domain should return invalid argument error", - inputRequest: &instance.AddCustomDomainRequest{ - InstanceId: instAndCtx.instance.ID(), - CustomDomain: " ", - }, - inputContext: ctxWithSysAuthZ, - expectedErrorCode: codes.InvalidArgument, - expectedErrorMsg: "Errors.Invalid.Argument", - }, - { - testName: "when valid request should return successful response", - inputRequest: &instance.AddCustomDomainRequest{ - InstanceId: instAndCtx.instance.ID(), - CustomDomain: " " + integration.DomainName(), - }, - inputContext: ctxWithSysAuthZ, - }, - } - - for _, tc := range tt { - t.Run(fmt.Sprintf("%s - %s", instAndCtx.testType, tc.testName), func(t *testing.T) { - t.Cleanup(func() { - if tc.expectedErrorMsg == "" { - instAndCtx.instance.Client.InstanceV2.RemoveCustomDomain(ctxWithSysAuthZ, &instance.RemoveCustomDomainRequest{CustomDomain: strings.TrimSpace(tc.inputRequest.CustomDomain)}) - } - }) - - // Test - res, err := instAndCtx.instance.Client.InstanceV2.AddCustomDomain(tc.inputContext, tc.inputRequest) - - // Verify - assert.Equal(t, tc.expectedErrorCode, status.Code(err)) - assert.Contains(t, status.Convert(err).Message(), tc.expectedErrorMsg) + for _, tc := range tt { + t.Run(tc.testName, func(t *testing.T) { + t.Cleanup(func() { if tc.expectedErrorMsg == "" { - assert.NotNil(t, res) - assert.NotEmpty(t, res.GetCreationDate()) + inst.Client.InstanceV2.RemoveCustomDomain(ctxWithSysAuthZ, &instance.RemoveCustomDomainRequest{CustomDomain: strings.TrimSpace(tc.inputRequest.CustomDomain)}) } }) - } + + // Test + res, err := inst.Client.InstanceV2.AddCustomDomain(tc.inputContext, tc.inputRequest) + + // Verify + assert.Equal(t, tc.expectedErrorCode, status.Code(err)) + assert.Contains(t, status.Convert(err).Message(), tc.expectedErrorMsg) + + if tc.expectedErrorMsg == "" { + assert.NotNil(t, res) + assert.NotEmpty(t, res.GetCreationDate()) + } + }) } } @@ -135,104 +114,80 @@ func TestRemoveCustomDomain(t *testing.T) { defer cancel() ctxWithSysAuthZ := integration.WithSystemAuthorization(ctx) - instES := integration.NewInstance(ctxWithSysAuthZ) - iamOwnerCtxES := instES.WithAuthorizationToken(t.Context(), integration.UserTypeIAMOwner) + inst := integration.NewInstance(ctxWithSysAuthZ) + iamOwnerCtx := inst.WithAuthorizationToken(t.Context(), integration.UserTypeIAMOwner) - instRelational := integration.NewInstance(ctxWithSysAuthZ) - iamOwnerCtxRelational := instRelational.WithAuthorizationToken(t.Context(), integration.UserTypeIAMOwner) - integration.EnsureInstanceFeature(t, ctxWithSysAuthZ, instRelational, &feature.SetInstanceFeaturesRequest{EnableRelationalTables: gu.Ptr(true)}, func(tCollect *assert.CollectT, got *feature.GetInstanceFeaturesResponse) { - assert.True(tCollect, got.EnableRelationalTables.GetEnabled()) - }) + customDomain := integration.DomainName() - customDomainES := integration.DomainName() - customDomainRelational := integration.DomainName() - - _, err := instES.Client.InstanceV2.AddCustomDomain(ctxWithSysAuthZ, &instance.AddCustomDomainRequest{InstanceId: instES.ID(), CustomDomain: customDomainES}) - require.Nil(t, err) - _, err = instRelational.Client.InstanceV2.AddCustomDomain(ctxWithSysAuthZ, &instance.AddCustomDomainRequest{InstanceId: instRelational.ID(), CustomDomain: customDomainRelational}) + _, err := inst.Client.InstanceV2.AddCustomDomain(ctxWithSysAuthZ, &instance.AddCustomDomainRequest{InstanceId: inst.ID(), CustomDomain: customDomain}) require.Nil(t, err) t.Cleanup(func() { - instES.Client.InstanceV2.RemoveCustomDomain(ctxWithSysAuthZ, &instance.RemoveCustomDomainRequest{InstanceId: instES.ID(), CustomDomain: customDomainES}) - instES.Client.InstanceV2.DeleteInstance(ctxWithSysAuthZ, &instance.DeleteInstanceRequest{InstanceId: instES.ID()}) - instRelational.Client.InstanceV2.RemoveCustomDomain(ctxWithSysAuthZ, &instance.RemoveCustomDomainRequest{InstanceId: instRelational.ID(), CustomDomain: customDomainRelational}) - instRelational.Client.InstanceV2.DeleteInstance(ctxWithSysAuthZ, &instance.DeleteInstanceRequest{InstanceId: instRelational.ID()}) + inst.Client.InstanceV2.RemoveCustomDomain(ctxWithSysAuthZ, &instance.RemoveCustomDomainRequest{InstanceId: inst.ID(), CustomDomain: customDomain}) + inst.Client.InstanceV2.DeleteInstance(ctxWithSysAuthZ, &instance.DeleteInstanceRequest{InstanceId: inst.ID()}) }) - type instanceAndCtx struct { - testType string - instance *integration.Instance - ctx context.Context - customDomain string - } - testedInstances := []instanceAndCtx{ - {testType: "eventstore", instance: instES, ctx: iamOwnerCtxES, customDomain: customDomainES}, - {testType: "relational", instance: instRelational, ctx: iamOwnerCtxRelational, customDomain: customDomainRelational}, + tt := []struct { + testName string + inputContext context.Context + inputRequest *instance.RemoveCustomDomainRequest + expectedErrorMsg string + expectedErrorCode codes.Code + }{ + { + testName: "when invalid context should return unauthN error", + inputRequest: &instance.RemoveCustomDomainRequest{ + InstanceId: inst.ID(), + CustomDomain: "custom1", + }, + inputContext: context.Background(), + expectedErrorCode: codes.Unauthenticated, + expectedErrorMsg: "auth header missing", + }, + { + testName: "when unauthZ context should return unauthZ error", + inputRequest: &instance.RemoveCustomDomainRequest{ + InstanceId: inst.ID(), + CustomDomain: "custom1", + }, + inputContext: iamOwnerCtx, + expectedErrorCode: codes.PermissionDenied, + expectedErrorMsg: "No matching permissions found (AUTH-5mWD2)", + }, + { + testName: "when invalid domain should return invalid argument error", + inputRequest: &instance.RemoveCustomDomainRequest{ + InstanceId: inst.ID(), + CustomDomain: " ", + }, + inputContext: ctxWithSysAuthZ, + expectedErrorCode: codes.InvalidArgument, + expectedErrorMsg: "Errors.Invalid.Argument", + }, + { + testName: "when valid request should return successful response", + inputRequest: &instance.RemoveCustomDomainRequest{ + InstanceId: inst.ID(), + CustomDomain: " " + customDomain, + }, + inputContext: ctxWithSysAuthZ, + }, } - for _, instAndCtx := range testedInstances { - tt := []struct { - testName string - inputContext context.Context - inputRequest *instance.RemoveCustomDomainRequest - expectedErrorMsg string - expectedErrorCode codes.Code - }{ - { - testName: "when invalid context should return unauthN error", - inputRequest: &instance.RemoveCustomDomainRequest{ - InstanceId: instAndCtx.instance.ID(), - CustomDomain: "custom1", - }, - inputContext: context.Background(), - expectedErrorCode: codes.Unauthenticated, - expectedErrorMsg: "auth header missing", - }, - { - testName: "when unauthZ context should return unauthZ error", - inputRequest: &instance.RemoveCustomDomainRequest{ - InstanceId: instAndCtx.instance.ID(), - CustomDomain: "custom1", - }, - inputContext: instAndCtx.ctx, - expectedErrorCode: codes.PermissionDenied, - expectedErrorMsg: "No matching permissions found (AUTH-5mWD2)", - }, - { - testName: "when invalid domain should return invalid argument error", - inputRequest: &instance.RemoveCustomDomainRequest{ - InstanceId: instAndCtx.instance.ID(), - CustomDomain: " ", - }, - inputContext: ctxWithSysAuthZ, - expectedErrorCode: codes.InvalidArgument, - expectedErrorMsg: "Errors.Invalid.Argument", - }, - { - testName: "when valid request should return successful response", - inputRequest: &instance.RemoveCustomDomainRequest{ - InstanceId: instAndCtx.instance.ID(), - CustomDomain: " " + instAndCtx.customDomain, - }, - inputContext: ctxWithSysAuthZ, - }, - } + for _, tc := range tt { + t.Run(tc.testName, func(t *testing.T) { + // Test + res, err := inst.Client.InstanceV2.RemoveCustomDomain(tc.inputContext, tc.inputRequest) - for _, tc := range tt { - t.Run(fmt.Sprintf("%s - %s", instAndCtx.testType, tc.testName), func(t *testing.T) { - // Test - res, err := instAndCtx.instance.Client.InstanceV2.RemoveCustomDomain(tc.inputContext, tc.inputRequest) + // Verify + assert.Equal(t, tc.expectedErrorCode, status.Code(err)) + assert.Contains(t, status.Convert(err).Message(), tc.expectedErrorMsg) - // Verify - assert.Equal(t, tc.expectedErrorCode, status.Code(err)) - assert.Contains(t, status.Convert(err).Message(), tc.expectedErrorMsg) - - if tc.expectedErrorMsg == "" { - assert.NotNil(t, res) - assert.NotEmpty(t, res.GetDeletionDate()) - } - }) - } + if tc.expectedErrorMsg == "" { + assert.NotNil(t, res) + assert.NotEmpty(t, res.GetDeletionDate()) + } + }) } } @@ -245,110 +200,81 @@ func TestAddTrustedDomain(t *testing.T) { ctxWithSysAuthZ := integration.WithSystemAuthorization(ctx) - // Eventstore objects - instES := integration.NewInstance(ctxWithSysAuthZ) - orgOwnerCtxES := instES.WithAuthorizationToken(context.Background(), integration.UserTypeOrgOwner) - d1ES := integration.DomainName() - - // Relational objects - instRelational := integration.NewInstance(ctxWithSysAuthZ) - integration.EnsureInstanceFeature(t, ctxWithSysAuthZ, instRelational, &feature.SetInstanceFeaturesRequest{EnableRelationalTables: gu.Ptr(true)}, func(tCollect *assert.CollectT, got *feature.GetInstanceFeaturesResponse) { - assert.True(tCollect, got.EnableRelationalTables.GetEnabled()) - }) - orgOwnerCtxRelational := instRelational.WithAuthorizationToken(context.Background(), integration.UserTypeOrgOwner) - d1Relational := integration.DomainName() + inst := integration.NewInstance(ctxWithSysAuthZ) + orgOwnerCtx := inst.WithAuthorizationToken(context.Background(), integration.UserTypeOrgOwner) + d1 := integration.DomainName() t.Cleanup(func() { - instES.Client.InstanceV2.DeleteInstance(ctxWithSysAuthZ, &instance.DeleteInstanceRequest{InstanceId: instES.ID()}) - instRelational.Client.InstanceV2.DeleteInstance(ctxWithSysAuthZ, &instance.DeleteInstanceRequest{InstanceId: instRelational.ID()}) + inst.Client.InstanceV2.DeleteInstance(ctxWithSysAuthZ, &instance.DeleteInstanceRequest{InstanceId: inst.ID()}) }) - type instAndCtx struct { - testType string - inst *integration.Instance - orgOwnerCtx context.Context - domain string + tt := []struct { + testName string + inputContext context.Context + inputRequest *instance.AddTrustedDomainRequest + expectedErrorMsg string + expectedErrorCode codes.Code + }{ + { + testName: "when invalid context should return unauthN error", + inputRequest: &instance.AddTrustedDomainRequest{ + InstanceId: inst.ID(), + TrustedDomain: d1, + }, + inputContext: context.Background(), + expectedErrorCode: codes.Unauthenticated, + expectedErrorMsg: "auth header missing", + }, + { + testName: "when unauthZ context should return unauthZ error", + inputRequest: &instance.AddTrustedDomainRequest{ + InstanceId: inst.ID(), + TrustedDomain: d1, + }, + inputContext: orgOwnerCtx, + expectedErrorCode: codes.NotFound, + expectedErrorMsg: "membership not found (AUTHZ-cdgFk)", + }, + { + testName: "when invalid domain should return invalid argument error", + inputRequest: &instance.AddTrustedDomainRequest{ + InstanceId: inst.ID(), + TrustedDomain: " ", + }, + inputContext: ctxWithSysAuthZ, + expectedErrorCode: codes.InvalidArgument, + expectedErrorMsg: "Errors.Invalid.Argument", + }, + { + testName: "when valid request should return successful response", + inputRequest: &instance.AddTrustedDomainRequest{ + InstanceId: inst.ID(), + TrustedDomain: " " + d1, + }, + inputContext: ctxWithSysAuthZ, + }, } - testedInstanceAndCtxs := []instAndCtx{ - {testType: "eventstore", inst: instES, orgOwnerCtx: orgOwnerCtxES, domain: d1ES}, - {testType: "relational", inst: instRelational, orgOwnerCtx: orgOwnerCtxRelational, domain: d1Relational}, - } - - for _, stateCase := range testedInstanceAndCtxs { - tt := []struct { - testName string - inputContext context.Context - inputRequest *instance.AddTrustedDomainRequest - expectedErrorMsg string - expectedErrorCode codes.Code - }{ - { - testName: "when invalid context should return unauthN error", - inputRequest: &instance.AddTrustedDomainRequest{ - InstanceId: stateCase.inst.ID(), - TrustedDomain: stateCase.domain, - }, - inputContext: context.Background(), - expectedErrorCode: codes.Unauthenticated, - expectedErrorMsg: "auth header missing", - }, - { - // TODO(IAM-Marco): Fix this test for relational case when permission checks are in place (see https://github.com/zitadel/zitadel/issues/10917) - testName: "when unauthZ context should return unauthZ error", - inputRequest: &instance.AddTrustedDomainRequest{ - InstanceId: stateCase.inst.ID(), - TrustedDomain: stateCase.domain, - }, - inputContext: stateCase.orgOwnerCtx, - expectedErrorCode: codes.NotFound, - expectedErrorMsg: "membership not found (AUTHZ-cdgFk)", - }, - { - testName: "when invalid domain should return invalid argument error", - inputRequest: &instance.AddTrustedDomainRequest{ - InstanceId: stateCase.inst.ID(), - TrustedDomain: " ", - }, - inputContext: ctxWithSysAuthZ, - expectedErrorCode: codes.InvalidArgument, - expectedErrorMsg: "Errors.Invalid.Argument", - }, - { - testName: "when valid request should return successful response", - inputRequest: &instance.AddTrustedDomainRequest{ - InstanceId: stateCase.inst.ID(), - TrustedDomain: " " + stateCase.domain, - }, - inputContext: ctxWithSysAuthZ, - }, - } - - for _, tc := range tt { - // TODO(IAM-Marco): Fix this test for relational case when permission checks are in place (see https://github.com/zitadel/zitadel/issues/10917) - if tc.testName == "when unauthZ context should return unauthZ error" && stateCase.testType == "relational" { - continue - } - t.Run(fmt.Sprintf("%s - %s", stateCase.testType, tc.testName), func(t *testing.T) { - t.Cleanup(func() { - if tc.expectedErrorMsg == "" { - stateCase.inst.Client.InstanceV2.RemoveTrustedDomain(ctxWithSysAuthZ, &instance.RemoveTrustedDomainRequest{TrustedDomain: strings.TrimSpace(tc.inputRequest.TrustedDomain)}) - } - }) - - // Test - res, err := stateCase.inst.Client.InstanceV2.AddTrustedDomain(tc.inputContext, tc.inputRequest) - - // Verify - assert.Equal(t, tc.expectedErrorCode, status.Code(err)) - assert.Contains(t, status.Convert(err).Message(), tc.expectedErrorMsg) - + for _, tc := range tt { + t.Run(tc.testName, func(t *testing.T) { + t.Cleanup(func() { if tc.expectedErrorMsg == "" { - assert.NotNil(t, res) - assert.NotEmpty(t, res.GetCreationDate()) + inst.Client.InstanceV2.RemoveTrustedDomain(ctxWithSysAuthZ, &instance.RemoveTrustedDomainRequest{TrustedDomain: strings.TrimSpace(tc.inputRequest.TrustedDomain)}) } }) - } + + // Test + res, err := inst.Client.InstanceV2.AddTrustedDomain(tc.inputContext, tc.inputRequest) + + // Verify + assert.Equal(t, tc.expectedErrorCode, status.Code(err)) + assert.Contains(t, status.Convert(err).Message(), tc.expectedErrorMsg) + + if tc.expectedErrorMsg == "" { + assert.NotNil(t, res) + assert.NotEmpty(t, res.GetCreationDate()) + } + }) } } @@ -361,100 +287,67 @@ func TestRemoveTrustedDomain(t *testing.T) { ctxWithSysAuthZ := integration.WithSystemAuthorization(ctx) - // Eventstore objects - instES := integration.NewInstance(ctxWithSysAuthZ) - orgOwnerCtxES := instES.WithAuthorizationToken(context.Background(), integration.UserTypeOrgOwner) - trustedDomainES := integration.DomainName() - _, err := instES.Client.InstanceV2.AddTrustedDomain(ctxWithSysAuthZ, &instance.AddTrustedDomainRequest{InstanceId: instES.ID(), TrustedDomain: trustedDomainES}) - require.Nil(t, err) - - // Relational objects - instRelational := integration.NewInstance(ctxWithSysAuthZ) - integration.EnsureInstanceFeature(t, ctxWithSysAuthZ, instRelational, &feature.SetInstanceFeaturesRequest{EnableRelationalTables: gu.Ptr(true)}, func(tCollect *assert.CollectT, got *feature.GetInstanceFeaturesResponse) { - assert.True(tCollect, got.EnableRelationalTables.GetEnabled()) - }) - orgOwnerCtxRelational := instRelational.WithAuthorizationToken(context.Background(), integration.UserTypeOrgOwner) - trustedDomainRelational := integration.DomainName() - _, err = instRelational.Client.InstanceV2.AddTrustedDomain(ctxWithSysAuthZ, &instance.AddTrustedDomainRequest{InstanceId: instRelational.ID(), TrustedDomain: trustedDomainRelational}) + inst := integration.NewInstance(ctxWithSysAuthZ) + orgOwnerCtx := inst.WithAuthorizationToken(context.Background(), integration.UserTypeOrgOwner) + trustedDomain := integration.DomainName() + _, err := inst.Client.InstanceV2.AddTrustedDomain(ctxWithSysAuthZ, &instance.AddTrustedDomainRequest{InstanceId: inst.ID(), TrustedDomain: trustedDomain}) require.Nil(t, err) t.Cleanup(func() { - instES.Client.InstanceV2.RemoveTrustedDomain(ctxWithSysAuthZ, &instance.RemoveTrustedDomainRequest{InstanceId: instES.ID(), TrustedDomain: trustedDomainES}) - instES.Client.InstanceV2.DeleteInstance(ctxWithSysAuthZ, &instance.DeleteInstanceRequest{InstanceId: instES.ID()}) - - instRelational.Client.InstanceV2.RemoveTrustedDomain(ctxWithSysAuthZ, &instance.RemoveTrustedDomainRequest{InstanceId: instRelational.ID(), TrustedDomain: trustedDomainRelational}) - instRelational.Client.InstanceV2.DeleteInstance(ctxWithSysAuthZ, &instance.DeleteInstanceRequest{InstanceId: instRelational.ID()}) + inst.Client.InstanceV2.RemoveTrustedDomain(ctxWithSysAuthZ, &instance.RemoveTrustedDomainRequest{InstanceId: inst.ID(), TrustedDomain: trustedDomain}) + inst.Client.InstanceV2.DeleteInstance(ctxWithSysAuthZ, &instance.DeleteInstanceRequest{InstanceId: inst.ID()}) }) - type instAndCtx struct { - testType string - inst *integration.Instance - orgOwnerCtx context.Context - domain string + tt := []struct { + testName string + inputContext context.Context + inputRequest *instance.RemoveTrustedDomainRequest + expectedErrorMsg string + expectedErrorCode codes.Code + }{ + { + testName: "when invalid context should return unauthN error", + inputRequest: &instance.RemoveTrustedDomainRequest{ + InstanceId: inst.ID(), + TrustedDomain: "trusted1", + }, + inputContext: context.Background(), + expectedErrorCode: codes.Unauthenticated, + expectedErrorMsg: "auth header missing", + }, + { + testName: "when unauthZ context should return unauthZ error", + inputRequest: &instance.RemoveTrustedDomainRequest{ + InstanceId: inst.ID(), + TrustedDomain: "trusted1", + }, + inputContext: orgOwnerCtx, + expectedErrorCode: codes.NotFound, + expectedErrorMsg: "membership not found (AUTHZ-cdgFk)", + }, + { + testName: "when valid request should return successful response", + inputRequest: &instance.RemoveTrustedDomainRequest{ + InstanceId: inst.ID(), + TrustedDomain: " " + trustedDomain, + }, + inputContext: ctxWithSysAuthZ, + }, } - testedInstanceAndCtxs := []instAndCtx{ - {testType: "eventstore", inst: instES, orgOwnerCtx: orgOwnerCtxES, domain: trustedDomainES}, - {testType: "relational", inst: instRelational, orgOwnerCtx: orgOwnerCtxRelational, domain: trustedDomainRelational}, - } + for _, tc := range tt { + t.Run(tc.testName, func(t *testing.T) { + // Test + res, err := inst.Client.InstanceV2.RemoveTrustedDomain(tc.inputContext, tc.inputRequest) - for _, stateCase := range testedInstanceAndCtxs { - tt := []struct { - testName string - inputContext context.Context - inputRequest *instance.RemoveTrustedDomainRequest - expectedErrorMsg string - expectedErrorCode codes.Code - }{ - { - testName: "when invalid context should return unauthN error", - inputRequest: &instance.RemoveTrustedDomainRequest{ - InstanceId: stateCase.inst.ID(), - TrustedDomain: "trusted1", - }, - inputContext: context.Background(), - expectedErrorCode: codes.Unauthenticated, - expectedErrorMsg: "auth header missing", - }, - { - // TODO(IAM-Marco): Fix this test for relational case when permission checks are in place (see https://github.com/zitadel/zitadel/issues/10917) testName: "when unauthZ context should return unauthZ error", - testName: "when unauthZ context should return unauthZ error", - inputRequest: &instance.RemoveTrustedDomainRequest{ - InstanceId: stateCase.inst.ID(), - TrustedDomain: "trusted1", - }, - inputContext: stateCase.orgOwnerCtx, - expectedErrorCode: codes.NotFound, - expectedErrorMsg: "membership not found (AUTHZ-cdgFk)", - }, - { - testName: "when valid request should return successful response", - inputRequest: &instance.RemoveTrustedDomainRequest{ - InstanceId: stateCase.inst.ID(), - TrustedDomain: " " + stateCase.domain, - }, - inputContext: ctxWithSysAuthZ, - }, - } + // Verify + assert.Equal(t, tc.expectedErrorCode, status.Code(err)) + assert.Equal(t, tc.expectedErrorMsg, status.Convert(err).Message()) - for _, tc := range tt { - // TODO(IAM-Marco): Fix this test for relational case when permission checks are in place (see https://github.com/zitadel/zitadel/issues/10917) - if tc.testName == "when unauthZ context should return unauthZ error" && stateCase.testType == "relational" { - continue + if tc.expectedErrorMsg == "" { + require.NotNil(t, res) + require.NotEmpty(t, res.GetDeletionDate()) } - t.Run(fmt.Sprintf("%s - %s", stateCase.testType, tc.testName), func(t *testing.T) { - // Test - res, err := stateCase.inst.Client.InstanceV2.RemoveTrustedDomain(tc.inputContext, tc.inputRequest) - - // Verify - assert.Equal(t, tc.expectedErrorCode, status.Code(err)) - assert.Equal(t, tc.expectedErrorMsg, status.Convert(err).Message()) - - if tc.expectedErrorMsg == "" { - require.NotNil(t, res) - require.NotEmpty(t, res.GetDeletionDate()) - } - }) - } + }) } } diff --git a/internal/api/grpc/instance/v2/integration_test/instance_test.go b/internal/api/grpc/instance/v2/integration_test/instance_test.go index 25ed4a1d0..81c492aaa 100644 --- a/internal/api/grpc/instance/v2/integration_test/instance_test.go +++ b/internal/api/grpc/instance/v2/integration_test/instance_test.go @@ -4,18 +4,15 @@ package instance_test import ( "context" - "fmt" "testing" "time" - "github.com/muhlemmer/gu" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "github.com/zitadel/zitadel/internal/integration" - "github.com/zitadel/zitadel/pkg/grpc/feature/v2" "github.com/zitadel/zitadel/pkg/grpc/instance/v2" ) @@ -28,135 +25,101 @@ func TestDeleteInstance(t *testing.T) { ctxWithSysAuthZ := integration.WithSystemAuthorization(ctx) - // Event store instances - instES := integration.NewInstance(ctxWithSysAuthZ) - instanceESOwnerCtx := instES.WithAuthorizationToken(t.Context(), integration.UserTypeIAMOwner) - instES2 := integration.NewInstance(ctxWithSysAuthZ) - - instRelational := integration.NewInstance(ctxWithSysAuthZ) - integration.EnsureInstanceFeature(t, ctxWithSysAuthZ, instRelational, &feature.SetInstanceFeaturesRequest{EnableRelationalTables: gu.Ptr(true)}, func(tCollect *assert.CollectT, got *feature.GetInstanceFeaturesResponse) { - assert.True(tCollect, got.EnableRelationalTables.GetEnabled()) - }) - instanceRelationalOwnerCtx := instRelational.WithAuthorizationToken(t.Context(), integration.UserTypeIAMOwner) - instRelational2 := integration.NewInstance(ctxWithSysAuthZ) - integration.EnsureInstanceFeature(t, ctxWithSysAuthZ, instRelational2, &feature.SetInstanceFeaturesRequest{EnableRelationalTables: gu.Ptr(true)}, func(tCollect *assert.CollectT, got *feature.GetInstanceFeaturesResponse) { - assert.True(tCollect, got.EnableRelationalTables.GetEnabled()) - }) - - type instAndCtx struct { - testType string - i1 *integration.Instance - i2 *integration.Instance - ctx context.Context - } - - testedInstancesAndCtxs := []instAndCtx{ - {testType: "eventstore", i1: instES, i2: instES2, ctx: instanceESOwnerCtx}, - {testType: "relational", i1: instRelational, i2: instRelational2, ctx: instanceRelationalOwnerCtx}, - } + inst := integration.NewInstance(ctxWithSysAuthZ) + instanceOwnerCtx := inst.WithAuthorizationToken(t.Context(), integration.UserTypeIAMOwner) + inst2 := integration.NewInstance(ctxWithSysAuthZ) t.Cleanup(func() { - instES.Client.InstanceV2.DeleteInstance(ctxWithSysAuthZ, &instance.DeleteInstanceRequest{InstanceId: instES.ID()}) - instES2.Client.InstanceV2.DeleteInstance(ctxWithSysAuthZ, &instance.DeleteInstanceRequest{InstanceId: instES2.ID()}) - instRelational.Client.InstanceV2.DeleteInstance(ctxWithSysAuthZ, &instance.DeleteInstanceRequest{InstanceId: instRelational.ID()}) - instRelational2.Client.InstanceV2.DeleteInstance(ctxWithSysAuthZ, &instance.DeleteInstanceRequest{InstanceId: instRelational2.ID()}) + inst.Client.InstanceV2.DeleteInstance(ctxWithSysAuthZ, &instance.DeleteInstanceRequest{InstanceId: inst.ID()}) + inst2.Client.InstanceV2.DeleteInstance(ctxWithSysAuthZ, &instance.DeleteInstanceRequest{InstanceId: inst2.ID()}) }) - for _, instanceWithCtx := range testedInstancesAndCtxs { - tt := []struct { - testName string - inputRequest *instance.DeleteInstanceRequest - inputContext context.Context - expectedErrorMsg string - expectedErrorCode codes.Code - expectedInstanceID string - expectsNullTimestamp bool - }{ - { - testName: "when invalid context should return unauthN error", - inputRequest: &instance.DeleteInstanceRequest{ - InstanceId: instanceWithCtx.i1.ID(), - }, - inputContext: t.Context(), - expectedErrorCode: codes.Unauthenticated, - expectedErrorMsg: "auth header missing", + tt := []struct { + testName string + inputRequest *instance.DeleteInstanceRequest + inputContext context.Context + expectedErrorMsg string + expectedErrorCode codes.Code + expectedInstanceID string + expectsNullTimestamp bool + }{ + { + testName: "when invalid context should return unauthN error", + inputRequest: &instance.DeleteInstanceRequest{ + InstanceId: inst.ID(), }, - { - testName: "when invalid context and invalid id should return unauthN error", - inputRequest: &instance.DeleteInstanceRequest{ - InstanceId: instanceWithCtx.i1.ID() + "invalid", - }, - inputContext: t.Context(), - expectedErrorCode: codes.Unauthenticated, - expectedErrorMsg: "auth header missing", + inputContext: t.Context(), + expectedErrorCode: codes.Unauthenticated, + expectedErrorMsg: "auth header missing", + }, + { + testName: "when invalid context and invalid id should return unauthN error", + inputRequest: &instance.DeleteInstanceRequest{ + InstanceId: inst.ID() + "invalid", }, - { - testName: "when instance token for invalid instance should return unauthN error", - inputRequest: &instance.DeleteInstanceRequest{ - InstanceId: instanceWithCtx.i1.ID() + "invalid", - }, - inputContext: instanceWithCtx.ctx, - expectedErrorCode: codes.Unauthenticated, - expectedErrorMsg: "Errors.Token.Invalid (AUTH-7fs1e)", + inputContext: t.Context(), + expectedErrorCode: codes.Unauthenticated, + expectedErrorMsg: "auth header missing", + }, + { + testName: "when instance token for invalid instance should return unauthN error", + inputRequest: &instance.DeleteInstanceRequest{ + InstanceId: inst.ID() + "invalid", }, - { - testName: "when instance token for other instance should return unauthN error", - inputRequest: &instance.DeleteInstanceRequest{ - InstanceId: instanceWithCtx.i2.ID(), - }, - inputContext: instanceWithCtx.ctx, - expectedErrorCode: codes.Unauthenticated, - expectedErrorMsg: "Errors.Token.Invalid (AUTH-7fs1e)", + inputContext: instanceOwnerCtx, + expectedErrorCode: codes.Unauthenticated, + expectedErrorMsg: "Errors.Token.Invalid (AUTH-7fs1e)", + }, + { + testName: "when instance token for other instance should return unauthN error", + inputRequest: &instance.DeleteInstanceRequest{ + InstanceId: inst2.ID(), }, - { - testName: "when instance token for own instance should return unauthZ error", - inputRequest: &instance.DeleteInstanceRequest{ - InstanceId: instanceWithCtx.i1.ID(), - }, - inputContext: instanceWithCtx.ctx, - expectedErrorCode: codes.PermissionDenied, - expectedErrorMsg: "No matching permissions found (AUTH-5mWD2)", + inputContext: instanceOwnerCtx, + expectedErrorCode: codes.Unauthenticated, + expectedErrorMsg: "Errors.Token.Invalid (AUTH-7fs1e)", + }, + { + testName: "when instance token for own instance should return unauthZ error", + inputRequest: &instance.DeleteInstanceRequest{ + InstanceId: inst.ID(), }, - { - // TODO(IAM-Marco): This test won't reach the relational side due to an interceptor - // automatically changing the instance in context to an empty one. - // The interceptor will look for the instance matching the instance ID passed here. - // Since the instance doesn't exist, it will put an empty instance in context. - // But an empty instance has no feature flags enabled. - testName: "when invalid id should return no error", - inputRequest: &instance.DeleteInstanceRequest{ - InstanceId: instanceWithCtx.i1.ID() + "invalid", - }, - inputContext: ctxWithSysAuthZ, - expectsNullTimestamp: true, + inputContext: instanceOwnerCtx, + expectedErrorCode: codes.PermissionDenied, + expectedErrorMsg: "No matching permissions found (AUTH-5mWD2)", + }, + { + testName: "when invalid id should return no error", + inputRequest: &instance.DeleteInstanceRequest{ + InstanceId: inst.ID() + "invalid", }, - { - testName: "when delete succeeds should return deletion date", - inputRequest: &instance.DeleteInstanceRequest{ - InstanceId: instanceWithCtx.i1.ID(), - }, - inputContext: ctxWithSysAuthZ, - expectedInstanceID: instanceWithCtx.i1.ID(), + inputContext: ctxWithSysAuthZ, + expectsNullTimestamp: true, + }, + { + testName: "when delete succeeds should return deletion date", + inputRequest: &instance.DeleteInstanceRequest{ + InstanceId: inst.ID(), }, - } - - for _, tc := range tt { - t.Run(fmt.Sprintf("%s - %s", instanceWithCtx.testType, tc.testName), func(t *testing.T) { - // Test - res, err := instanceWithCtx.i1.Client.InstanceV2.DeleteInstance(tc.inputContext, tc.inputRequest) - - // Verify - assert.Equal(t, tc.expectedErrorCode, status.Code(err)) - assert.Equal(t, tc.expectedErrorMsg, status.Convert(err).Message()) - if tc.expectedErrorMsg == "" { - require.NotNil(t, res) - require.Equal(t, tc.expectsNullTimestamp, res.GetDeletionDate() == nil) - } - }) - } - + inputContext: ctxWithSysAuthZ, + expectedInstanceID: inst.ID(), + }, } + for _, tc := range tt { + t.Run(tc.testName, func(t *testing.T) { + // Test + res, err := inst.Client.InstanceV2.DeleteInstance(tc.inputContext, tc.inputRequest) + + // Verify + assert.Equal(t, tc.expectedErrorCode, status.Code(err)) + assert.Equal(t, tc.expectedErrorMsg, status.Convert(err).Message()) + if tc.expectedErrorMsg == "" { + require.NotNil(t, res) + require.Equal(t, tc.expectsNullTimestamp, res.GetDeletionDate() == nil) + } + }) + } } func TestUpdateInstance(t *testing.T) { @@ -168,163 +131,124 @@ func TestUpdateInstance(t *testing.T) { ctxWithSysAuthZ := integration.WithSystemAuthorization(ctx) - instES := integration.NewInstance(ctxWithSysAuthZ) - instanceOwnerCtxES := instES.WithAuthorizationToken(context.Background(), integration.UserTypeIAMOwner) - orgOwnerCtxES := instES.WithAuthorizationToken(context.Background(), integration.UserTypeOrgOwner) - inst2ES := integration.NewInstance(ctxWithSysAuthZ) - - // Relational - instRelational := integration.NewInstance(ctxWithSysAuthZ) - integration.EnsureInstanceFeature(t, ctxWithSysAuthZ, instRelational, &feature.SetInstanceFeaturesRequest{EnableRelationalTables: gu.Ptr(true)}, func(tCollect *assert.CollectT, got *feature.GetInstanceFeaturesResponse) { - assert.True(tCollect, got.EnableRelationalTables.GetEnabled()) - }) - instanceOwnerCtxRelational := instRelational.WithAuthorizationToken(context.Background(), integration.UserTypeIAMOwner) - orgOwnerCtxRelational := instRelational.WithAuthorizationToken(context.Background(), integration.UserTypeOrgOwner) - inst2Relational := integration.NewInstance(ctxWithSysAuthZ) - integration.EnsureInstanceFeature(t, ctxWithSysAuthZ, inst2Relational, &feature.SetInstanceFeaturesRequest{EnableRelationalTables: gu.Ptr(true)}, func(tCollect *assert.CollectT, got *feature.GetInstanceFeaturesResponse) { - assert.True(tCollect, got.EnableRelationalTables.GetEnabled()) - }) + inst := integration.NewInstance(ctxWithSysAuthZ) + instanceOwnerCtx := inst.WithAuthorizationToken(context.Background(), integration.UserTypeIAMOwner) + orgOwnerCtx := inst.WithAuthorizationToken(context.Background(), integration.UserTypeOrgOwner) + inst2 := integration.NewInstance(ctxWithSysAuthZ) t.Cleanup(func() { - instES.Client.InstanceV2.DeleteInstance(ctxWithSysAuthZ, &instance.DeleteInstanceRequest{InstanceId: instES.ID()}) - inst2ES.Client.InstanceV2.DeleteInstance(ctxWithSysAuthZ, &instance.DeleteInstanceRequest{InstanceId: inst2ES.ID()}) - instRelational.Client.InstanceV2.DeleteInstance(ctxWithSysAuthZ, &instance.DeleteInstanceRequest{InstanceId: instRelational.ID()}) - inst2Relational.Client.InstanceV2.DeleteInstance(ctxWithSysAuthZ, &instance.DeleteInstanceRequest{InstanceId: inst2Relational.ID()}) + inst.Client.InstanceV2.DeleteInstance(ctxWithSysAuthZ, &instance.DeleteInstanceRequest{InstanceId: inst.ID()}) + inst2.Client.InstanceV2.DeleteInstance(ctxWithSysAuthZ, &instance.DeleteInstanceRequest{InstanceId: inst2.ID()}) }) - type instAndCtx struct { - testType string - inst *integration.Instance - inst2 *integration.Instance - instanceOwnerCtx context.Context - orgOwnerCtx context.Context + tt := []struct { + testName string + inputRequest *instance.UpdateInstanceRequest + inputContext context.Context + expectedErrorMsg string + expectedErrorCode codes.Code + expectedNewName string + }{ + { + testName: "when invalid context should return unauthN error", + inputRequest: &instance.UpdateInstanceRequest{ + InstanceId: inst.ID(), + InstanceName: " ", + }, + inputContext: context.Background(), + expectedErrorCode: codes.Unauthenticated, + expectedErrorMsg: "auth header missing", + }, + { + testName: "when invalid context and invalid id should return unauthN error", + inputRequest: &instance.UpdateInstanceRequest{ + InstanceId: inst.ID() + "invalid", + InstanceName: " ", + }, + inputContext: context.Background(), + expectedErrorCode: codes.Unauthenticated, + expectedErrorMsg: "auth header missing", + }, + { + testName: "when instance token for invalid instance should return unauthN error", + inputRequest: &instance.UpdateInstanceRequest{ + InstanceId: inst.ID() + "invalid", + InstanceName: " ", + }, + inputContext: instanceOwnerCtx, + expectedErrorCode: codes.Unauthenticated, + expectedErrorMsg: "Errors.Token.Invalid (AUTH-7fs1e)", + }, + { + testName: "when instance token for other instance should return unauthN error", + inputRequest: &instance.UpdateInstanceRequest{ + InstanceId: inst2.ID(), + InstanceName: " ", + }, + inputContext: instanceOwnerCtx, + expectedErrorCode: codes.Unauthenticated, + expectedErrorMsg: "Errors.Token.Invalid (AUTH-7fs1e)", + }, + { + testName: "when unauthZ context should return unauthZ error", + inputRequest: &instance.UpdateInstanceRequest{ + InstanceId: inst.ID(), + InstanceName: "a valid name", + }, + inputContext: orgOwnerCtx, + expectedErrorCode: codes.NotFound, + expectedErrorMsg: "membership not found (AUTHZ-cdgFk)", + }, + { + testName: "when invalid id should return not found error", + inputRequest: &instance.UpdateInstanceRequest{ + InstanceId: inst.ID() + "invalid", + InstanceName: "an-updated-name", + }, + inputContext: ctxWithSysAuthZ, + expectedErrorCode: codes.NotFound, + expectedErrorMsg: "Errors.Instance.NotFound (INST-nuso2m)", + }, + { + testName: "when update succeeds (instance owner) should change instance name", + inputRequest: &instance.UpdateInstanceRequest{ + InstanceId: inst.ID(), + InstanceName: "an-updated-name", + }, + inputContext: instanceOwnerCtx, + expectedNewName: "an-updated-name", + }, + { + testName: "when update succeeds should change instance name", + inputRequest: &instance.UpdateInstanceRequest{ + InstanceId: inst2.ID(), + InstanceName: "an-updated-name2", + }, + inputContext: ctxWithSysAuthZ, + expectedNewName: "an-updated-name2", + }, } - testedInstancesAndCtxs := []instAndCtx{ - {testType: "eventstore", inst: instES, inst2: inst2ES, instanceOwnerCtx: instanceOwnerCtxES, orgOwnerCtx: orgOwnerCtxES}, - {testType: "relational", inst: instRelational, inst2: inst2Relational, instanceOwnerCtx: instanceOwnerCtxRelational, orgOwnerCtx: orgOwnerCtxRelational}, - } + for _, tc := range tt { + t.Run(tc.testName, func(t *testing.T) { + // Test + res, err := inst.Client.InstanceV2.UpdateInstance(tc.inputContext, tc.inputRequest) - for _, instancesWithCtx := range testedInstancesAndCtxs { - tt := []struct { - testName string - inputRequest *instance.UpdateInstanceRequest - inputContext context.Context - expectedErrorMsg string - expectedErrorCode codes.Code - expectedNewName string - }{ - { - testName: "when invalid context should return unauthN error", - inputRequest: &instance.UpdateInstanceRequest{ - InstanceId: instancesWithCtx.inst.ID(), - InstanceName: " ", - }, - inputContext: context.Background(), - expectedErrorCode: codes.Unauthenticated, - expectedErrorMsg: "auth header missing", - }, - { - testName: "when invalid context and invalid id should return unauthN error", - inputRequest: &instance.UpdateInstanceRequest{ - InstanceId: instancesWithCtx.inst.ID() + "invalid", - InstanceName: " ", - }, - inputContext: context.Background(), - expectedErrorCode: codes.Unauthenticated, - expectedErrorMsg: "auth header missing", - }, - { - testName: "when instance token for invalid instance should return unauthN error", - inputRequest: &instance.UpdateInstanceRequest{ - InstanceId: instancesWithCtx.inst.ID() + "invalid", - InstanceName: " ", - }, - inputContext: instancesWithCtx.instanceOwnerCtx, - expectedErrorCode: codes.Unauthenticated, - expectedErrorMsg: "Errors.Token.Invalid (AUTH-7fs1e)", - }, - { - testName: "when instance token for other instance should return unauthN error", - inputRequest: &instance.UpdateInstanceRequest{ - InstanceId: instancesWithCtx.inst2.ID(), - InstanceName: " ", - }, - inputContext: instancesWithCtx.instanceOwnerCtx, - expectedErrorCode: codes.Unauthenticated, - expectedErrorMsg: "Errors.Token.Invalid (AUTH-7fs1e)", - }, - { - // TODO(IAM-Marco): Fix this test for relational case when permission checks are in place (see https://github.com/zitadel/zitadel/issues/10917) - testName: "when unauthZ context should return unauthZ error", - inputRequest: &instance.UpdateInstanceRequest{ - InstanceId: instancesWithCtx.inst.ID(), - InstanceName: "a valid name", - }, - inputContext: instancesWithCtx.orgOwnerCtx, - expectedErrorCode: codes.NotFound, - expectedErrorMsg: "membership not found (AUTHZ-cdgFk)", - }, - { - // TODO(IAM-Marco): This test won't reach the relational side due to an interceptor - // automatically changing the instance in context to an empty one. - // The interceptor will look for the instance matching the instance ID passed here. - // Since the instance doesn't exist, it will put an empty instance in context. - // But an empty instance has no feature flags enabled. - testName: "when invalid id should return not found error", - inputRequest: &instance.UpdateInstanceRequest{ - InstanceId: instancesWithCtx.inst.ID() + "invalid", - InstanceName: "an-updated-name", - }, - inputContext: ctxWithSysAuthZ, - expectedErrorCode: codes.NotFound, - expectedErrorMsg: "Errors.Instance.NotFound (INST-nuso2m)", - }, - { - testName: "when update succeeds (instance owner) should change instance name", - inputRequest: &instance.UpdateInstanceRequest{ - InstanceId: instancesWithCtx.inst.ID(), - InstanceName: "an-updated-name", - }, - inputContext: instancesWithCtx.instanceOwnerCtx, - expectedNewName: "an-updated-name", - }, - { - testName: "when update succeeds should change instance name", - inputRequest: &instance.UpdateInstanceRequest{ - InstanceId: instancesWithCtx.inst2.ID(), - InstanceName: "an-updated-name2", - }, - inputContext: ctxWithSysAuthZ, - expectedNewName: "an-updated-name2", - }, - } + // Verify + assert.Equal(t, tc.expectedErrorCode, status.Code(err)) + assert.Equal(t, tc.expectedErrorMsg, status.Convert(err).Message()) + if tc.expectedErrorMsg == "" { - for _, tc := range tt { - // TODO(IAM-Marco): Fix this test for relational case when permission checks are in place (see https://github.com/zitadel/zitadel/issues/10917) - if tc.testName == "when unauthZ context should return unauthZ error" && instancesWithCtx.testType == "relational" { - continue + require.NotNil(t, res) + assert.NotEmpty(t, res.GetChangeDate()) + + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(tc.inputContext, time.Minute) + require.EventuallyWithT(t, func(tt *assert.CollectT) { + retrievedInstance, err := inst.Client.InstanceV2.GetInstance(tc.inputContext, &instance.GetInstanceRequest{InstanceId: tc.inputRequest.GetInstanceId()}) + require.Nil(tt, err) + assert.Equal(tt, tc.expectedNewName, retrievedInstance.GetInstance().GetName()) + }, retryDuration, tick, "timeout waiting for expected execution result") } - t.Run(fmt.Sprintf("%s - %s", instancesWithCtx.testType, tc.testName), func(t *testing.T) { - // Test - res, err := instES.Client.InstanceV2.UpdateInstance(tc.inputContext, tc.inputRequest) - - // Verify - assert.Equal(t, tc.expectedErrorCode, status.Code(err)) - assert.Equal(t, tc.expectedErrorMsg, status.Convert(err).Message()) - if tc.expectedErrorMsg == "" { - - require.NotNil(t, res) - assert.NotEmpty(t, res.GetChangeDate()) - - retryDuration, tick := integration.WaitForAndTickWithMaxDuration(tc.inputContext, time.Minute) - require.EventuallyWithT(t, func(tt *assert.CollectT) { - retrievedInstance, err := instES.Client.InstanceV2.GetInstance(tc.inputContext, &instance.GetInstanceRequest{InstanceId: tc.inputRequest.GetInstanceId()}) - require.Nil(tt, err) - assert.Equal(tt, tc.expectedNewName, retrievedInstance.GetInstance().GetName()) - }, retryDuration, tick, "timeout waiting for expected execution result") - } - }) - } + }) } } diff --git a/internal/api/grpc/instance/v2/integration_test/query_test.go b/internal/api/grpc/instance/v2/integration_test/query_test.go index 657a76e00..a468bdf75 100644 --- a/internal/api/grpc/instance/v2/integration_test/query_test.go +++ b/internal/api/grpc/instance/v2/integration_test/query_test.go @@ -4,12 +4,9 @@ package instance_test import ( "context" - "fmt" - "slices" "testing" "time" - "github.com/muhlemmer/gu" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "google.golang.org/grpc/codes" @@ -30,110 +27,70 @@ func TestGetInstance(t *testing.T) { defer cancel() ctxWithSysAuthZ := integration.WithSystemAuthorization(ctx) - instES := integration.NewInstance(ctxWithSysAuthZ) - instanceOwnerCtxES := instES.WithAuthorizationToken(context.Background(), integration.UserTypeIAMOwner) - organizationOwnerCtxES := instES.WithAuthorizationToken(context.Background(), integration.UserTypeOrgOwner) - - // Relational - instRelational := integration.NewInstance(ctxWithSysAuthZ) - integration.EnsureInstanceFeature(t, ctxWithSysAuthZ, instRelational, &feature.SetInstanceFeaturesRequest{EnableRelationalTables: gu.Ptr(true)}, func(tCollect *assert.CollectT, got *feature.GetInstanceFeaturesResponse) { - assert.True(tCollect, got.EnableRelationalTables.GetEnabled()) - }) - instanceOwnerCtxRelational := instRelational.WithAuthorizationToken(context.Background(), integration.UserTypeIAMOwner) - organizationOwnerCtxRelational := instRelational.WithAuthorizationToken(context.Background(), integration.UserTypeOrgOwner) + inst := integration.NewInstance(ctxWithSysAuthZ) + instanceOwnerCtx := inst.WithAuthorizationToken(context.Background(), integration.UserTypeIAMOwner) + organizationOwnerCtx := inst.WithAuthorizationToken(context.Background(), integration.UserTypeOrgOwner) t.Cleanup(func() { - instES.Client.InstanceV2.DeleteInstance(ctxWithSysAuthZ, &instance.DeleteInstanceRequest{InstanceId: instES.ID()}) - instRelational.Client.InstanceV2.DeleteInstance(ctxWithSysAuthZ, &instance.DeleteInstanceRequest{InstanceId: instRelational.ID()}) + inst.Client.InstanceV2.DeleteInstance(ctxWithSysAuthZ, &instance.DeleteInstanceRequest{InstanceId: inst.ID()}) }) - type instAndCtx struct { - testType string - inst *integration.Instance - instanceOwnerCtx context.Context - orgOwnerCtx context.Context + tt := []struct { + testName string + inputContext context.Context + inputInstanceID string + expectedInstanceID string + expectedErrorMsg string + expectedErrorCode codes.Code + }{ + { + testName: "when unauthN context should return unauthN error", + inputContext: context.Background(), + inputInstanceID: inst.ID(), + expectedErrorCode: codes.Unauthenticated, + expectedErrorMsg: "auth header missing", + }, + { + testName: "when unauthZ context should return unauthZ error", + inputContext: organizationOwnerCtx, + inputInstanceID: inst.ID(), + expectedErrorCode: codes.NotFound, + expectedErrorMsg: "membership not found (AUTHZ-cdgFk)", + }, + { + testName: "when request succeeds should return matching instance (systemUser)", + inputContext: ctxWithSysAuthZ, + inputInstanceID: inst.ID(), + expectedInstanceID: inst.ID(), + }, + { + testName: "when request succeeds should return matching instance (own context)", + inputContext: instanceOwnerCtx, + expectedInstanceID: inst.ID(), + }, + { + testName: "when instance not found should return not found error", + inputContext: ctxWithSysAuthZ, + inputInstanceID: "invalid", + expectedErrorCode: codes.NotFound, + expectedErrorMsg: "Errors.Instance.NotFound (QUERY-n0wng)", + }, } - testedInstanceAndCtxs := []instAndCtx{ - {testType: "eventstore", inst: instES, instanceOwnerCtx: instanceOwnerCtxES, orgOwnerCtx: organizationOwnerCtxES}, - {testType: "relational", inst: instRelational, instanceOwnerCtx: instanceOwnerCtxRelational, orgOwnerCtx: organizationOwnerCtxRelational}, - } + for _, tc := range tt { + t.Run(tc.testName, func(t *testing.T) { + // Test + res, err := inst.Client.InstanceV2.GetInstance(tc.inputContext, &instance.GetInstanceRequest{InstanceId: tc.inputInstanceID}) - for _, instWithCtx := range testedInstanceAndCtxs { - tt := []struct { - testName string - inputContext context.Context - inputInstanceID string - expectedInstanceID string - expectedErrorMsg string - expectedErrorCode codes.Code - }{ - { - testName: "when unauthN context should return unauthN error", - inputContext: context.Background(), - inputInstanceID: instWithCtx.inst.ID(), - expectedErrorCode: codes.Unauthenticated, - expectedErrorMsg: "auth header missing", - }, - { - // TODO(IAM-Marco): Fix this test for relational case when permission checks are in place (see https://github.com/zitadel/zitadel/issues/10917) - testName: "when unauthZ context should return unauthZ error", - inputContext: instWithCtx.orgOwnerCtx, - inputInstanceID: instWithCtx.inst.ID(), - expectedErrorCode: codes.NotFound, - expectedErrorMsg: "membership not found (AUTHZ-cdgFk)", - }, - { - testName: "when request succeeds should return matching instance (systemUser)", - inputContext: ctxWithSysAuthZ, - inputInstanceID: instWithCtx.inst.ID(), - expectedInstanceID: instWithCtx.inst.ID(), - }, - { - // TODO(IAM-Marco): Decide if we should set the instance in context. - testName: "when request succeeds should return matching instance (own context)", - inputContext: instWithCtx.instanceOwnerCtx, - expectedInstanceID: instWithCtx.inst.ID(), - }, - { - // TODO(IAM-Marco): This test won't reach the relational side due to an interceptor - // automatically changing the instance in context to an empty one. - // The interceptor will look for the instance matching the instance ID passed here. - // Since the instance doesn't exist, it will put an empty instance in context. - // But an empty instance has no feature flags enabled. - testName: "when instance not found should return not found error", - inputContext: ctxWithSysAuthZ, - inputInstanceID: "invalid", - expectedErrorCode: codes.NotFound, - expectedErrorMsg: "Errors.Instance.NotFound (QUERY-n0wng)", - }, - } + // Verify + assert.Equal(t, tc.expectedErrorCode, status.Code(err)) + assert.Equal(t, tc.expectedErrorMsg, status.Convert(err).Message()) - faultyTestCasesForRelational := []string{ - // TODO(IAM-Marco): Fix this test for relational case when permission checks are in place (see https://github.com/zitadel/zitadel/issues/10917) - "when unauthZ context should return unauthZ error", - // TODO(IAM-Marco): Decide if we should set the instance in context. - "when request succeeds should return matching instance (own context)", - } - - for _, tc := range tt { - if instWithCtx.testType == "relational" && slices.Contains(faultyTestCasesForRelational, tc.testName) { - continue + if tc.expectedErrorMsg == "" { + require.NoError(t, err) + assert.Equal(t, tc.expectedInstanceID, res.GetInstance().GetId()) } - t.Run(fmt.Sprintf("%s - %s", instWithCtx.testType, tc.testName), func(t *testing.T) { - // Test - res, err := instWithCtx.inst.Client.InstanceV2.GetInstance(tc.inputContext, &instance.GetInstanceRequest{InstanceId: tc.inputInstanceID}) - - // Verify - assert.Equal(t, tc.expectedErrorCode, status.Code(err)) - assert.Equal(t, tc.expectedErrorMsg, status.Convert(err).Message()) - - if tc.expectedErrorMsg == "" { - require.NoError(t, err) - assert.Equal(t, tc.expectedInstanceID, res.GetInstance().GetId()) - } - }) - } + }) } } @@ -165,8 +122,6 @@ func TestListInstances(t *testing.T) { orgOwnerCtx := inst.WithAuthorizationToken(context.Background(), integration.UserTypeOrgOwner) - relTableState := integration.RelationalTablesEnableMatrix(t, ctx, ctxWithSysAuthZ) - tt := []struct { testName string inputRequest *instance.ListInstancesRequest @@ -185,7 +140,6 @@ func TestListInstances(t *testing.T) { expectedErrorMsg: "auth header missing", }, { - // TODO(IAM-Marco): Fix this test for relational case when permission checks are in place (see https://github.com/zitadel/zitadel/issues/10917) testName: "when unauthZ context should return unauthZ error", inputRequest: &instance.ListInstancesRequest{ Pagination: &filter.PaginationRequest{Offset: 0, Limit: 10}, @@ -221,32 +175,25 @@ func TestListInstances(t *testing.T) { }, } - for _, stateCase := range relTableState { + for _, tc := range tt { + t.Run(tc.testName, func(t *testing.T) { + // Test + res, err := inst.Client.InstanceV2.ListInstances(tc.inputContext, tc.inputRequest) - for _, tc := range tt { - // TODO(IAM-Marco): Fix this test for relational case when permission checks are in place (see https://github.com/zitadel/zitadel/issues/10917) - if tc.testName == "when unauthZ context should return unauthZ error" && stateCase.Name == "when relational tables are enabled" { - continue - } - t.Run(fmt.Sprintf("%s - %s", stateCase.Name, tc.testName), func(t *testing.T) { - // Test - res, err := inst.Client.InstanceV2.ListInstances(tc.inputContext, tc.inputRequest) + // Verify + assert.Equal(t, tc.expectedErrorCode, status.Code(err)) + assert.Equal(t, tc.expectedErrorMsg, status.Convert(err).Message()) - // Verify - assert.Equal(t, tc.expectedErrorCode, status.Code(err)) - assert.Equal(t, tc.expectedErrorMsg, status.Convert(err).Message()) + if tc.expectedErrorMsg == "" { + require.NotNil(t, res) - if tc.expectedErrorMsg == "" { - require.NotNil(t, res) + require.Len(t, res.GetInstances(), len(tc.expectedInstances)) - require.Len(t, res.GetInstances(), len(tc.expectedInstances)) - - for i, ins := range res.GetInstances() { - assert.Equal(t, tc.expectedInstances[i], ins.GetId()) - } + for i, ins := range res.GetInstances() { + assert.Equal(t, tc.expectedInstances[i], ins.GetId()) } - }) - } + } + }) } } @@ -259,149 +206,115 @@ func TestListCustomDomains(t *testing.T) { ctxWithSysAuthZ := integration.WithSystemAuthorization(ctx) - // Eventstore objects - instES := integration.NewInstance(ctxWithSysAuthZ) - orgOwnerCtxES := instES.WithAuthorizationToken(context.Background(), integration.UserTypeOrgOwner) - instanceOwnerCtxES := instES.WithAuthorizationToken(context.Background(), integration.UserTypeIAMOwner) - d1ES, d2ES := "custom."+integration.DomainName(), "custom."+integration.DomainName() - _, err := instES.Client.InstanceV2.AddCustomDomain(ctxWithSysAuthZ, &instance.AddCustomDomainRequest{InstanceId: instES.ID(), CustomDomain: d1ES}) + inst := integration.NewInstance(ctxWithSysAuthZ) + orgOwnerCtx := inst.WithAuthorizationToken(context.Background(), integration.UserTypeOrgOwner) + instanceOwnerCtx := inst.WithAuthorizationToken(context.Background(), integration.UserTypeIAMOwner) + d1, d2 := "custom."+integration.DomainName(), "custom."+integration.DomainName() + _, err := inst.Client.InstanceV2.AddCustomDomain(ctxWithSysAuthZ, &instance.AddCustomDomainRequest{InstanceId: inst.ID(), CustomDomain: d1}) require.Nil(t, err) - _, err = instES.Client.InstanceV2.AddCustomDomain(ctxWithSysAuthZ, &instance.AddCustomDomainRequest{InstanceId: instES.ID(), CustomDomain: d2ES}) - require.Nil(t, err) - - // Relational objects - instRelational := integration.NewInstance(ctxWithSysAuthZ) - integration.EnsureInstanceFeature(t, ctx, instRelational, &feature.SetInstanceFeaturesRequest{EnableRelationalTables: gu.Ptr(true)}, func(tCollect *assert.CollectT, got *feature.GetInstanceFeaturesResponse) { - assert.True(tCollect, got.EnableRelationalTables.GetEnabled()) - }) - orgOwnerCtxRelational := instRelational.WithAuthorizationToken(context.Background(), integration.UserTypeOrgOwner) - instanceOwnerCtxRelational := instRelational.WithAuthorizationToken(context.Background(), integration.UserTypeIAMOwner) - d1Relational, d2Relational := "custom."+integration.DomainName(), "custom."+integration.DomainName() - _, err = instRelational.Client.InstanceV2.AddCustomDomain(ctxWithSysAuthZ, &instance.AddCustomDomainRequest{InstanceId: instRelational.ID(), CustomDomain: d1Relational}) - require.Nil(t, err) - _, err = instRelational.Client.InstanceV2.AddCustomDomain(ctxWithSysAuthZ, &instance.AddCustomDomainRequest{InstanceId: instRelational.ID(), CustomDomain: d2Relational}) + _, err = inst.Client.InstanceV2.AddCustomDomain(ctxWithSysAuthZ, &instance.AddCustomDomainRequest{InstanceId: inst.ID(), CustomDomain: d2}) require.Nil(t, err) t.Cleanup(func() { - instES.Client.InstanceV2.RemoveCustomDomain(ctxWithSysAuthZ, &instance.RemoveCustomDomainRequest{InstanceId: instES.ID(), CustomDomain: d1ES}) - instES.Client.InstanceV2.RemoveCustomDomain(ctxWithSysAuthZ, &instance.RemoveCustomDomainRequest{InstanceId: instES.ID(), CustomDomain: d2ES}) - instES.Client.InstanceV2.DeleteInstance(ctxWithSysAuthZ, &instance.DeleteInstanceRequest{InstanceId: instES.ID()}) - - instRelational.Client.InstanceV2.RemoveCustomDomain(ctxWithSysAuthZ, &instance.RemoveCustomDomainRequest{InstanceId: instRelational.ID(), CustomDomain: d1Relational}) - instRelational.Client.InstanceV2.RemoveCustomDomain(ctxWithSysAuthZ, &instance.RemoveCustomDomainRequest{InstanceId: instRelational.ID(), CustomDomain: d2Relational}) - instRelational.Client.InstanceV2.DeleteInstance(ctxWithSysAuthZ, &instance.DeleteInstanceRequest{InstanceId: instRelational.ID()}) + inst.Client.InstanceV2.RemoveCustomDomain(ctxWithSysAuthZ, &instance.RemoveCustomDomainRequest{InstanceId: inst.ID(), CustomDomain: d1}) + inst.Client.InstanceV2.RemoveCustomDomain(ctxWithSysAuthZ, &instance.RemoveCustomDomainRequest{InstanceId: inst.ID(), CustomDomain: d2}) + inst.Client.InstanceV2.DeleteInstance(ctxWithSysAuthZ, &instance.DeleteInstanceRequest{InstanceId: inst.ID()}) }) - testData := []struct { - testType string - inst *integration.Instance - sysOwnerCtx context.Context - instOwnerCtx context.Context - orgOwnerCtx context.Context - expectedDomains []string + expectedDomains := []string{d1, d2} + + tt := []struct { + testName string + inputRequest *instance.ListCustomDomainsRequest + inputContext context.Context + expectedErrorMsg string + expectedErrorCode codes.Code + expectedDomains []string }{ - {testType: "eventstore", inst: instES, sysOwnerCtx: ctxWithSysAuthZ, instOwnerCtx: instanceOwnerCtxES, orgOwnerCtx: orgOwnerCtxES, expectedDomains: []string{d1ES, d2ES}}, - {testType: "relational", inst: instRelational, sysOwnerCtx: ctxWithSysAuthZ, instOwnerCtx: instanceOwnerCtxRelational, orgOwnerCtx: orgOwnerCtxRelational, expectedDomains: []string{d1Relational, d2Relational}}, + { + testName: "when invalid context should return unauthN error", + inputRequest: &instance.ListCustomDomainsRequest{ + InstanceId: inst.ID(), + Pagination: &filter.PaginationRequest{Offset: 0, Limit: 10}, + }, + inputContext: context.Background(), + expectedErrorCode: codes.Unauthenticated, + expectedErrorMsg: "auth header missing", + }, + { + testName: "when unauthZ context should return unauthZ error", + inputRequest: &instance.ListCustomDomainsRequest{ + InstanceId: inst.ID(), + Pagination: &filter.PaginationRequest{Offset: 0, Limit: 10}, + SortingColumn: instance.DomainFieldName_DOMAIN_FIELD_NAME_CREATION_DATE, + Filters: []*instance.CustomDomainFilter{ + { + Filter: &instance.CustomDomainFilter_DomainFilter{ + DomainFilter: &instance.DomainFilter{Domain: "custom", Method: object.TextQueryMethod_TEXT_QUERY_METHOD_CONTAINS}, + }, + }, + }, + }, + inputContext: orgOwnerCtx, + expectedErrorCode: codes.NotFound, + expectedErrorMsg: "membership not found (AUTHZ-cdgFk)", + }, + { + testName: "when valid request with filter should return paginated response (systemUser)", + inputRequest: &instance.ListCustomDomainsRequest{ + InstanceId: inst.ID(), + Pagination: &filter.PaginationRequest{Offset: 0, Limit: 10}, + SortingColumn: instance.DomainFieldName_DOMAIN_FIELD_NAME_CREATION_DATE, + Filters: []*instance.CustomDomainFilter{ + { + Filter: &instance.CustomDomainFilter_DomainFilter{ + DomainFilter: &instance.DomainFilter{Domain: "custom", Method: object.TextQueryMethod_TEXT_QUERY_METHOD_CONTAINS}, + }, + }, + }, + }, + inputContext: ctxWithSysAuthZ, + expectedDomains: expectedDomains, + }, + { + testName: "when valid request with filter should return paginated response (own context)", + inputRequest: &instance.ListCustomDomainsRequest{ + Pagination: &filter.PaginationRequest{Offset: 0, Limit: 10}, + SortingColumn: instance.DomainFieldName_DOMAIN_FIELD_NAME_CREATION_DATE, + Filters: []*instance.CustomDomainFilter{ + { + Filter: &instance.CustomDomainFilter_DomainFilter{ + DomainFilter: &instance.DomainFilter{Domain: "custom", Method: object.TextQueryMethod_TEXT_QUERY_METHOD_CONTAINS}, + }, + }, + }, + }, + inputContext: instanceOwnerCtx, + expectedDomains: []string{expectedDomains[0]}, + }, } - for _, td := range testData { - tt := []struct { - testName string - inputRequest *instance.ListCustomDomainsRequest - inputContext context.Context - expectedErrorMsg string - expectedErrorCode codes.Code - expectedDomains []string - }{ - { - testName: "when invalid context should return unauthN error", - inputRequest: &instance.ListCustomDomainsRequest{ - InstanceId: td.inst.ID(), - Pagination: &filter.PaginationRequest{Offset: 0, Limit: 10}, - }, - inputContext: context.Background(), - expectedErrorCode: codes.Unauthenticated, - expectedErrorMsg: "auth header missing", - }, - { - // TODO(IAM-Marco): Fix this test for relational case when permission checks are in place (see https://github.com/zitadel/zitadel/issues/10917) - testName: "when unauthZ context should return unauthZ error", - inputRequest: &instance.ListCustomDomainsRequest{ - InstanceId: td.inst.ID(), - Pagination: &filter.PaginationRequest{Offset: 0, Limit: 10}, - SortingColumn: instance.DomainFieldName_DOMAIN_FIELD_NAME_CREATION_DATE, - Filters: []*instance.CustomDomainFilter{ - { - Filter: &instance.CustomDomainFilter_DomainFilter{ - DomainFilter: &instance.DomainFilter{Domain: "custom", Method: object.TextQueryMethod_TEXT_QUERY_METHOD_CONTAINS}, - }, - }, - }, - }, - inputContext: td.orgOwnerCtx, - expectedErrorCode: codes.NotFound, - expectedErrorMsg: "membership not found (AUTHZ-cdgFk)", - }, - { - testName: "when valid request with filter should return paginated response (systemUser)", - inputRequest: &instance.ListCustomDomainsRequest{ - InstanceId: td.inst.ID(), - Pagination: &filter.PaginationRequest{Offset: 0, Limit: 10}, - SortingColumn: instance.DomainFieldName_DOMAIN_FIELD_NAME_CREATION_DATE, - Filters: []*instance.CustomDomainFilter{ - { - Filter: &instance.CustomDomainFilter_DomainFilter{ - DomainFilter: &instance.DomainFilter{Domain: "custom", Method: object.TextQueryMethod_TEXT_QUERY_METHOD_CONTAINS}, - }, - }, - }, - }, - inputContext: td.sysOwnerCtx, - expectedDomains: td.expectedDomains, - }, - { - testName: "when valid request with filter should return paginated response (own context)", - inputRequest: &instance.ListCustomDomainsRequest{ - Pagination: &filter.PaginationRequest{Offset: 0, Limit: 10}, - SortingColumn: instance.DomainFieldName_DOMAIN_FIELD_NAME_CREATION_DATE, - Filters: []*instance.CustomDomainFilter{ - { - Filter: &instance.CustomDomainFilter_DomainFilter{ - DomainFilter: &instance.DomainFilter{Domain: "custom", Method: object.TextQueryMethod_TEXT_QUERY_METHOD_CONTAINS}, - }, - }, - }, - }, - inputContext: td.instOwnerCtx, - expectedDomains: []string{td.expectedDomains[0]}, - }, - } + for _, tc := range tt { + t.Run(tc.testName, func(t *testing.T) { + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(tc.inputContext, time.Minute) + require.EventuallyWithT(t, func(collect *assert.CollectT) { + // Test + res, err := inst.Client.InstanceV2.ListCustomDomains(tc.inputContext, tc.inputRequest) - for _, tc := range tt { - if tc.testName == "when unauthZ context should return unauthZ error" && td.testType == "relational" { - continue - } - t.Run(fmt.Sprintf("%s - %s", td.testType, tc.testName), func(t *testing.T) { - retryDuration, tick := integration.WaitForAndTickWithMaxDuration(tc.inputContext, time.Minute) - require.EventuallyWithT(t, func(collect *assert.CollectT) { - // Test - res, err := td.inst.Client.InstanceV2.ListCustomDomains(tc.inputContext, tc.inputRequest) + // Verify + assert.Equal(collect, tc.expectedErrorCode, status.Code(err)) + assert.Equal(collect, tc.expectedErrorMsg, status.Convert(err).Message()) - // Verify - assert.Equal(collect, tc.expectedErrorCode, status.Code(err)) - assert.Equal(collect, tc.expectedErrorMsg, status.Convert(err).Message()) - - if tc.expectedErrorMsg == "" { - domains := []string{} - for _, d := range res.GetDomains() { - domains = append(domains, d.GetDomain()) - } - - assert.Subset(collect, domains, tc.expectedDomains) + if tc.expectedErrorMsg == "" { + domains := []string{} + for _, d := range res.GetDomains() { + domains = append(domains, d.GetDomain()) } - }, retryDuration, tick) - }) - } + + assert.Subset(collect, domains, tc.expectedDomains) + } + }, retryDuration, tick) + }) } } @@ -414,143 +327,108 @@ func TestListTrustedDomains(t *testing.T) { ctxWithSysAuthZ := integration.WithSystemAuthorization(ctx) - // Eventstore objects - instES := integration.NewInstance(ctxWithSysAuthZ) - orgOwnerCtxES := instES.WithAuthorizationToken(context.Background(), integration.UserTypeOrgOwner) - instanceOwnerCtxES := instES.WithAuthorizationToken(context.Background(), integration.UserTypeIAMOwner) - d1ES, d2ES := "trusted."+integration.DomainName(), "trusted."+integration.DomainName() - _, err := instES.Client.InstanceV2.AddTrustedDomain(ctxWithSysAuthZ, &instance.AddTrustedDomainRequest{InstanceId: instES.ID(), TrustedDomain: d1ES}) + inst := integration.NewInstance(ctxWithSysAuthZ) + orgOwnerCtx := inst.WithAuthorizationToken(context.Background(), integration.UserTypeOrgOwner) + instanceOwnerCtx := inst.WithAuthorizationToken(context.Background(), integration.UserTypeIAMOwner) + d1, d2 := "trusted."+integration.DomainName(), "trusted."+integration.DomainName() + _, err := inst.Client.InstanceV2.AddTrustedDomain(ctxWithSysAuthZ, &instance.AddTrustedDomainRequest{InstanceId: inst.ID(), TrustedDomain: d1}) require.Nil(t, err) - _, err = instES.Client.InstanceV2.AddTrustedDomain(ctxWithSysAuthZ, &instance.AddTrustedDomainRequest{InstanceId: instES.ID(), TrustedDomain: d2ES}) - require.Nil(t, err) - - // Relational objects - instRelational := integration.NewInstance(ctxWithSysAuthZ) - integration.EnsureInstanceFeature(t, ctx, instRelational, &feature.SetInstanceFeaturesRequest{EnableRelationalTables: gu.Ptr(true)}, func(tCollect *assert.CollectT, got *feature.GetInstanceFeaturesResponse) { - assert.True(tCollect, got.EnableRelationalTables.GetEnabled()) - }) - orgOwnerCtxRelational := instRelational.WithAuthorizationToken(context.Background(), integration.UserTypeOrgOwner) - instanceOwnerCtxRelational := instRelational.WithAuthorizationToken(context.Background(), integration.UserTypeIAMOwner) - d1Relational, d2Relational := "trusted."+integration.DomainName(), "trusted."+integration.DomainName() - _, err = instRelational.Client.InstanceV2.AddTrustedDomain(ctxWithSysAuthZ, &instance.AddTrustedDomainRequest{InstanceId: instRelational.ID(), TrustedDomain: d1Relational}) - require.Nil(t, err) - _, err = instRelational.Client.InstanceV2.AddTrustedDomain(ctxWithSysAuthZ, &instance.AddTrustedDomainRequest{InstanceId: instRelational.ID(), TrustedDomain: d2Relational}) + _, err = inst.Client.InstanceV2.AddTrustedDomain(ctxWithSysAuthZ, &instance.AddTrustedDomainRequest{InstanceId: inst.ID(), TrustedDomain: d2}) require.Nil(t, err) t.Cleanup(func() { - instES.Client.InstanceV2.RemoveTrustedDomain(ctxWithSysAuthZ, &instance.RemoveTrustedDomainRequest{InstanceId: instES.ID(), TrustedDomain: d1ES}) - instES.Client.InstanceV2.RemoveTrustedDomain(ctxWithSysAuthZ, &instance.RemoveTrustedDomainRequest{InstanceId: instES.ID(), TrustedDomain: d2ES}) - instES.Client.InstanceV2.DeleteInstance(ctxWithSysAuthZ, &instance.DeleteInstanceRequest{InstanceId: instES.ID()}) - - instRelational.Client.InstanceV2.RemoveTrustedDomain(ctxWithSysAuthZ, &instance.RemoveTrustedDomainRequest{InstanceId: instRelational.ID(), TrustedDomain: d1Relational}) - instRelational.Client.InstanceV2.RemoveTrustedDomain(ctxWithSysAuthZ, &instance.RemoveTrustedDomainRequest{InstanceId: instRelational.ID(), TrustedDomain: d2Relational}) - instRelational.Client.InstanceV2.DeleteInstance(ctxWithSysAuthZ, &instance.DeleteInstanceRequest{InstanceId: instRelational.ID()}) + inst.Client.InstanceV2.RemoveTrustedDomain(ctxWithSysAuthZ, &instance.RemoveTrustedDomainRequest{InstanceId: inst.ID(), TrustedDomain: d1}) + inst.Client.InstanceV2.RemoveTrustedDomain(ctxWithSysAuthZ, &instance.RemoveTrustedDomainRequest{InstanceId: inst.ID(), TrustedDomain: d2}) + inst.Client.InstanceV2.DeleteInstance(ctxWithSysAuthZ, &instance.DeleteInstanceRequest{InstanceId: inst.ID()}) }) - testData := []struct { - testType string - inst *integration.Instance - sysOwnerCtx context.Context - instOwnerCtx context.Context - orgOwnerCtx context.Context - expectedDomains []string + expectedDomains := []string{d1, d2} + + tt := []struct { + testName string + inputRequest *instance.ListTrustedDomainsRequest + inputContext context.Context + expectedErrorMsg string + expectedErrorCode codes.Code + expectedDomains []string }{ - {testType: "eventstore", inst: instES, sysOwnerCtx: ctxWithSysAuthZ, instOwnerCtx: instanceOwnerCtxES, orgOwnerCtx: orgOwnerCtxES, expectedDomains: []string{d1ES, d2ES}}, - {testType: "relational", inst: instRelational, sysOwnerCtx: ctxWithSysAuthZ, instOwnerCtx: instanceOwnerCtxRelational, orgOwnerCtx: orgOwnerCtxRelational, expectedDomains: []string{d1Relational, d2Relational}}, + { + testName: "when invalid context should return unauthN error", + inputRequest: &instance.ListTrustedDomainsRequest{ + InstanceId: inst.ID(), + Pagination: &filter.PaginationRequest{Offset: 0, Limit: 10}, + }, + inputContext: context.Background(), + expectedErrorCode: codes.Unauthenticated, + expectedErrorMsg: "auth header missing", + }, + { + testName: "when unauthZ context should return unauthZ error", + inputRequest: &instance.ListTrustedDomainsRequest{ + InstanceId: inst.ID(), + Pagination: &filter.PaginationRequest{Offset: 0, Limit: 10}, + }, + inputContext: orgOwnerCtx, + expectedErrorCode: codes.NotFound, + expectedErrorMsg: "membership not found (AUTHZ-cdgFk)", + }, + { + testName: "when valid request with filter should return paginated response (systemUser)", + inputRequest: &instance.ListTrustedDomainsRequest{ + InstanceId: inst.ID(), + Pagination: &filter.PaginationRequest{Offset: 0, Limit: 10}, + SortingColumn: instance.TrustedDomainFieldName_TRUSTED_DOMAIN_FIELD_NAME_CREATION_DATE, + Filters: []*instance.TrustedDomainFilter{ + { + Filter: &instance.TrustedDomainFilter_DomainFilter{ + DomainFilter: &instance.DomainFilter{Domain: "trusted", Method: object.TextQueryMethod_TEXT_QUERY_METHOD_CONTAINS}, + }, + }, + }, + }, + inputContext: ctxWithSysAuthZ, + expectedDomains: expectedDomains, + }, + { + testName: "when valid request with filter should return paginated response (own context)", + inputRequest: &instance.ListTrustedDomainsRequest{ + Pagination: &filter.PaginationRequest{Offset: 0, Limit: 10}, + SortingColumn: instance.TrustedDomainFieldName_TRUSTED_DOMAIN_FIELD_NAME_CREATION_DATE, + Filters: []*instance.TrustedDomainFilter{ + { + Filter: &instance.TrustedDomainFilter_DomainFilter{ + DomainFilter: &instance.DomainFilter{Domain: "trusted", Method: object.TextQueryMethod_TEXT_QUERY_METHOD_CONTAINS}, + }, + }, + }, + }, + inputContext: instanceOwnerCtx, + expectedDomains: []string{expectedDomains[0]}, + }, } - for _, td := range testData { - tt := []struct { - testName string - inputRequest *instance.ListTrustedDomainsRequest - inputContext context.Context - expectedErrorMsg string - expectedErrorCode codes.Code - expectedDomains []string - }{ - { - testName: "when invalid context should return unauthN error", - inputRequest: &instance.ListTrustedDomainsRequest{ - InstanceId: td.inst.ID(), - Pagination: &filter.PaginationRequest{Offset: 0, Limit: 10}, - }, - inputContext: context.Background(), - expectedErrorCode: codes.Unauthenticated, - expectedErrorMsg: "auth header missing", - }, - { - // TODO(IAM-Marco): Fix this test for relational case when permission checks are in place (see https://github.com/zitadel/zitadel/issues/10917) - testName: "when unauthZ context should return unauthZ error", - inputRequest: &instance.ListTrustedDomainsRequest{ - InstanceId: td.inst.ID(), - Pagination: &filter.PaginationRequest{Offset: 0, Limit: 10}, - }, - inputContext: td.orgOwnerCtx, - expectedErrorCode: codes.NotFound, - expectedErrorMsg: "membership not found (AUTHZ-cdgFk)", - }, - { - testName: "when valid request with filter should return paginated response (systemUser)", - inputRequest: &instance.ListTrustedDomainsRequest{ - InstanceId: td.inst.ID(), - Pagination: &filter.PaginationRequest{Offset: 0, Limit: 10}, - SortingColumn: instance.TrustedDomainFieldName_TRUSTED_DOMAIN_FIELD_NAME_CREATION_DATE, - Filters: []*instance.TrustedDomainFilter{ - { - Filter: &instance.TrustedDomainFilter_DomainFilter{ - DomainFilter: &instance.DomainFilter{Domain: "trusted", Method: object.TextQueryMethod_TEXT_QUERY_METHOD_CONTAINS}, - }, - }, - }, - }, - inputContext: td.sysOwnerCtx, - expectedDomains: td.expectedDomains, - }, - { - testName: "when valid request with filter should return paginated response (own context)", - inputRequest: &instance.ListTrustedDomainsRequest{ - Pagination: &filter.PaginationRequest{Offset: 0, Limit: 10}, - SortingColumn: instance.TrustedDomainFieldName_TRUSTED_DOMAIN_FIELD_NAME_CREATION_DATE, - Filters: []*instance.TrustedDomainFilter{ - { - Filter: &instance.TrustedDomainFilter_DomainFilter{ - DomainFilter: &instance.DomainFilter{Domain: "trusted", Method: object.TextQueryMethod_TEXT_QUERY_METHOD_CONTAINS}, - }, - }, - }, - }, - inputContext: td.instOwnerCtx, - expectedDomains: []string{td.expectedDomains[0]}, - }, - } - for _, tc := range tt { - // TODO(IAM-Marco): Fix this test for relational case when permission checks are in place (see https://github.com/zitadel/zitadel/issues/10917) - if tc.testName == "when unauthZ context should return unauthZ error" && td.testType == "relational" { - continue - } + for _, tc := range tt { + t.Run(tc.testName, func(t *testing.T) { + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(tc.inputContext, time.Minute) + require.EventuallyWithT(t, func(collect *assert.CollectT) { + // Test + res, err := inst.Client.InstanceV2.ListTrustedDomains(tc.inputContext, tc.inputRequest) - t.Run(fmt.Sprintf("%s - %s", td.testType, tc.testName), func(t *testing.T) { - retryDuration, tick := integration.WaitForAndTickWithMaxDuration(tc.inputContext, time.Minute) - require.EventuallyWithT(t, func(collect *assert.CollectT) { - // Test - res, err := td.inst.Client.InstanceV2.ListTrustedDomains(tc.inputContext, tc.inputRequest) + // Verify + assert.Equal(collect, tc.expectedErrorCode, status.Code(err)) + assert.Equal(collect, tc.expectedErrorMsg, status.Convert(err).Message()) - // Verify - assert.Equal(collect, tc.expectedErrorCode, status.Code(err)) - assert.Equal(collect, tc.expectedErrorMsg, status.Convert(err).Message()) + if tc.expectedErrorMsg == "" { + require.NotNil(t, res) - if tc.expectedErrorMsg == "" { - require.NotNil(t, res) - - domains := []string{} - for _, d := range res.GetTrustedDomain() { - domains = append(domains, d.GetDomain()) - } - - assert.Subset(collect, domains, tc.expectedDomains) + domains := []string{} + for _, d := range res.GetTrustedDomain() { + domains = append(domains, d.GetDomain()) } - }, retryDuration, tick) - }) - } + + assert.Subset(collect, domains, tc.expectedDomains) + } + }, retryDuration, tick) + }) } } diff --git a/internal/api/grpc/instance/v2beta/integration_test/domain_test.go b/internal/api/grpc/instance/v2beta/integration_test/domain_test.go index 8cd24fa7a..736192fd7 100644 --- a/internal/api/grpc/instance/v2beta/integration_test/domain_test.go +++ b/internal/api/grpc/instance/v2beta/integration_test/domain_test.go @@ -4,19 +4,16 @@ package instance_test import ( "context" - "fmt" "strings" "testing" "time" - "github.com/muhlemmer/gu" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "github.com/zitadel/zitadel/internal/integration" - "github.com/zitadel/zitadel/pkg/grpc/feature/v2" instance "github.com/zitadel/zitadel/pkg/grpc/instance/v2beta" ) @@ -32,99 +29,77 @@ func TestAddCustomDomain(t *testing.T) { inst := integration.NewInstance(ctxWithSysAuthZ) iamOwnerCtx := inst.WithAuthorizationToken(context.Background(), integration.UserTypeIAMOwner) - relationalInst := integration.NewInstance(ctxWithSysAuthZ) - iamOwnerRelationalCtx := relationalInst.WithAuthorizationToken(context.Background(), integration.UserTypeIAMOwner) - integration.EnsureInstanceFeature(t, ctxWithSysAuthZ, relationalInst, - &feature.SetInstanceFeaturesRequest{EnableRelationalTables: gu.Ptr(true)}, - func(tCollect *assert.CollectT, got *feature.GetInstanceFeaturesResponse) { - assert.True(tCollect, got.EnableRelationalTables.GetEnabled()) - }, - ) - t.Cleanup(func() { inst.Client.InstanceV2Beta.DeleteInstance(ctxWithSysAuthZ, &instance.DeleteInstanceRequest{InstanceId: inst.ID()}) - relationalInst.Client.InstanceV2Beta.DeleteInstance(ctxWithSysAuthZ, &instance.DeleteInstanceRequest{InstanceId: relationalInst.ID()}) }) - type instanceAndCtx struct { - testType string - instance *integration.Instance - ctx context.Context - } - testedInstances := []instanceAndCtx{ - {testType: "eventstore", instance: inst, ctx: iamOwnerCtx}, - {testType: "relational", instance: relationalInst, ctx: iamOwnerRelationalCtx}, + tt := []struct { + testName string + inputContext context.Context + inputRequest *instance.AddCustomDomainRequest + expectedErrorMsg string + expectedErrorCode codes.Code + }{ + { + testName: "when invalid context should return unauthN error", + inputRequest: &instance.AddCustomDomainRequest{ + InstanceId: inst.ID(), + Domain: integration.DomainName(), + }, + inputContext: context.Background(), + expectedErrorCode: codes.Unauthenticated, + expectedErrorMsg: "auth header missing", + }, + { + testName: "when unauthZ context should return unauthZ error", + inputRequest: &instance.AddCustomDomainRequest{ + InstanceId: inst.ID(), + Domain: integration.DomainName(), + }, + inputContext: iamOwnerCtx, + expectedErrorCode: codes.PermissionDenied, + expectedErrorMsg: "No matching permissions found (AUTH-5mWD2)", + }, + { + testName: "when invalid domain should return invalid argument error", + inputRequest: &instance.AddCustomDomainRequest{ + InstanceId: inst.ID(), + Domain: " ", + }, + inputContext: ctxWithSysAuthZ, + expectedErrorCode: codes.InvalidArgument, + expectedErrorMsg: "Errors.Invalid.Argument", + }, + { + testName: "when valid request should return successful response", + inputRequest: &instance.AddCustomDomainRequest{ + InstanceId: inst.ID(), + Domain: " " + integration.DomainName(), + }, + inputContext: ctxWithSysAuthZ, + }, } - for _, instAndCtx := range testedInstances { - tt := []struct { - testName string - inputContext context.Context - inputRequest *instance.AddCustomDomainRequest - expectedErrorMsg string - expectedErrorCode codes.Code - }{ - { - testName: "when invalid context should return unauthN error", - inputRequest: &instance.AddCustomDomainRequest{ - InstanceId: instAndCtx.instance.ID(), - Domain: integration.DomainName(), - }, - inputContext: context.Background(), - expectedErrorCode: codes.Unauthenticated, - expectedErrorMsg: "auth header missing", - }, - { - testName: "when unauthZ context should return unauthZ error", - inputRequest: &instance.AddCustomDomainRequest{ - InstanceId: instAndCtx.instance.ID(), - Domain: integration.DomainName(), - }, - inputContext: instAndCtx.ctx, - expectedErrorCode: codes.PermissionDenied, - expectedErrorMsg: "No matching permissions found (AUTH-5mWD2)", - }, - { - testName: "when invalid domain should return invalid argument error", - inputRequest: &instance.AddCustomDomainRequest{ - InstanceId: instAndCtx.instance.ID(), - Domain: " ", - }, - inputContext: ctxWithSysAuthZ, - expectedErrorCode: codes.InvalidArgument, - expectedErrorMsg: "Errors.Invalid.Argument", - }, - { - testName: "when valid request should return successful response", - inputRequest: &instance.AddCustomDomainRequest{ - InstanceId: instAndCtx.instance.ID(), - Domain: " " + integration.DomainName(), - }, - inputContext: ctxWithSysAuthZ, - }, - } - - for _, tc := range tt { - t.Run(fmt.Sprintf("%s - %s", instAndCtx.testType, tc.testName), func(t *testing.T) { - t.Cleanup(func() { - if tc.expectedErrorMsg == "" { - instAndCtx.instance.Client.InstanceV2Beta.RemoveCustomDomain(ctxWithSysAuthZ, &instance.RemoveCustomDomainRequest{Domain: strings.TrimSpace(tc.inputRequest.Domain)}) - } - }) - - // Test - res, err := instAndCtx.instance.Client.InstanceV2Beta.AddCustomDomain(tc.inputContext, tc.inputRequest) - - // Verify - assert.Equal(t, tc.expectedErrorCode, status.Code(err)) - assert.Contains(t, status.Convert(err).Message(), tc.expectedErrorMsg) - + for _, tc := range tt { + t.Run(tc.testName, func(t *testing.T) { + t.Cleanup(func() { if tc.expectedErrorMsg == "" { - assert.NotNil(t, res) - assert.NotEmpty(t, res.GetCreationDate()) + inst.Client.InstanceV2Beta.RemoveCustomDomain(ctxWithSysAuthZ, &instance.RemoveCustomDomainRequest{Domain: strings.TrimSpace(tc.inputRequest.Domain)}) } }) - } + + // Test + res, err := inst.Client.InstanceV2Beta.AddCustomDomain(tc.inputContext, tc.inputRequest) + + // Verify + assert.Equal(t, tc.expectedErrorCode, status.Code(err)) + assert.Contains(t, status.Convert(err).Message(), tc.expectedErrorMsg) + + if tc.expectedErrorMsg == "" { + assert.NotNil(t, res) + assert.NotEmpty(t, res.GetCreationDate()) + } + }) } } @@ -136,104 +111,80 @@ func TestRemoveCustomDomain(t *testing.T) { defer cancel() ctxWithSysAuthZ := integration.WithSystemAuthorization(ctx) - instES := integration.NewInstance(ctxWithSysAuthZ) - iamOwnerCtxES := instES.WithAuthorizationToken(t.Context(), integration.UserTypeIAMOwner) + inst := integration.NewInstance(ctxWithSysAuthZ) + iamOwnerCtx := inst.WithAuthorizationToken(t.Context(), integration.UserTypeIAMOwner) - instRelational := integration.NewInstance(ctxWithSysAuthZ) - iamOwnerCtxRelational := instRelational.WithAuthorizationToken(t.Context(), integration.UserTypeIAMOwner) - integration.EnsureInstanceFeature(t, ctxWithSysAuthZ, instRelational, &feature.SetInstanceFeaturesRequest{EnableRelationalTables: gu.Ptr(true)}, func(tCollect *assert.CollectT, got *feature.GetInstanceFeaturesResponse) { - assert.True(tCollect, got.EnableRelationalTables.GetEnabled()) - }) + customDomain := integration.DomainName() - customDomainES := integration.DomainName() - customDomainRelational := integration.DomainName() - - _, err := instES.Client.InstanceV2Beta.AddCustomDomain(ctxWithSysAuthZ, &instance.AddCustomDomainRequest{InstanceId: instES.ID(), Domain: customDomainES}) - require.Nil(t, err) - _, err = instRelational.Client.InstanceV2Beta.AddCustomDomain(ctxWithSysAuthZ, &instance.AddCustomDomainRequest{InstanceId: instRelational.ID(), Domain: customDomainRelational}) + _, err := inst.Client.InstanceV2Beta.AddCustomDomain(ctxWithSysAuthZ, &instance.AddCustomDomainRequest{InstanceId: inst.ID(), Domain: customDomain}) require.Nil(t, err) t.Cleanup(func() { - instES.Client.InstanceV2Beta.RemoveCustomDomain(ctxWithSysAuthZ, &instance.RemoveCustomDomainRequest{InstanceId: instES.ID(), Domain: customDomainES}) - instES.Client.InstanceV2Beta.DeleteInstance(ctxWithSysAuthZ, &instance.DeleteInstanceRequest{InstanceId: instES.ID()}) - instRelational.Client.InstanceV2Beta.RemoveCustomDomain(ctxWithSysAuthZ, &instance.RemoveCustomDomainRequest{InstanceId: instRelational.ID(), Domain: customDomainRelational}) - instRelational.Client.InstanceV2Beta.DeleteInstance(ctxWithSysAuthZ, &instance.DeleteInstanceRequest{InstanceId: instRelational.ID()}) + inst.Client.InstanceV2Beta.RemoveCustomDomain(ctxWithSysAuthZ, &instance.RemoveCustomDomainRequest{InstanceId: inst.ID(), Domain: customDomain}) + inst.Client.InstanceV2Beta.DeleteInstance(ctxWithSysAuthZ, &instance.DeleteInstanceRequest{InstanceId: inst.ID()}) }) - type instanceAndCtx struct { - testType string - instance *integration.Instance - ctx context.Context - customDomain string - } - testedInstances := []instanceAndCtx{ - {testType: "eventstore", instance: instES, ctx: iamOwnerCtxES, customDomain: customDomainES}, - {testType: "relational", instance: instRelational, ctx: iamOwnerCtxRelational, customDomain: customDomainRelational}, + tt := []struct { + testName string + inputContext context.Context + inputRequest *instance.RemoveCustomDomainRequest + expectedErrorMsg string + expectedErrorCode codes.Code + }{ + { + testName: "when invalid context should return unauthN error", + inputRequest: &instance.RemoveCustomDomainRequest{ + InstanceId: inst.ID(), + Domain: "custom1", + }, + inputContext: context.Background(), + expectedErrorCode: codes.Unauthenticated, + expectedErrorMsg: "auth header missing", + }, + { + testName: "when unauthZ context should return unauthZ error", + inputRequest: &instance.RemoveCustomDomainRequest{ + InstanceId: inst.ID(), + Domain: "custom1", + }, + inputContext: iamOwnerCtx, + expectedErrorCode: codes.PermissionDenied, + expectedErrorMsg: "No matching permissions found (AUTH-5mWD2)", + }, + { + testName: "when invalid domain should return invalid argument error", + inputRequest: &instance.RemoveCustomDomainRequest{ + InstanceId: inst.ID(), + Domain: " ", + }, + inputContext: ctxWithSysAuthZ, + expectedErrorCode: codes.InvalidArgument, + expectedErrorMsg: "Errors.Invalid.Argument", + }, + { + testName: "when valid request should return successful response", + inputRequest: &instance.RemoveCustomDomainRequest{ + InstanceId: inst.ID(), + Domain: " " + customDomain, + }, + inputContext: ctxWithSysAuthZ, + }, } - for _, instAndCtx := range testedInstances { - tt := []struct { - testName string - inputContext context.Context - inputRequest *instance.RemoveCustomDomainRequest - expectedErrorMsg string - expectedErrorCode codes.Code - }{ - { - testName: "when invalid context should return unauthN error", - inputRequest: &instance.RemoveCustomDomainRequest{ - InstanceId: instAndCtx.instance.ID(), - Domain: "custom1", - }, - inputContext: context.Background(), - expectedErrorCode: codes.Unauthenticated, - expectedErrorMsg: "auth header missing", - }, - { - testName: "when unauthZ context should return unauthZ error", - inputRequest: &instance.RemoveCustomDomainRequest{ - InstanceId: instAndCtx.instance.ID(), - Domain: "custom1", - }, - inputContext: instAndCtx.ctx, - expectedErrorCode: codes.PermissionDenied, - expectedErrorMsg: "No matching permissions found (AUTH-5mWD2)", - }, - { - testName: "when invalid domain should return invalid argument error", - inputRequest: &instance.RemoveCustomDomainRequest{ - InstanceId: instAndCtx.instance.ID(), - Domain: " ", - }, - inputContext: ctxWithSysAuthZ, - expectedErrorCode: codes.InvalidArgument, - expectedErrorMsg: "Errors.Invalid.Argument", - }, - { - testName: "when valid request should return successful response", - inputRequest: &instance.RemoveCustomDomainRequest{ - InstanceId: instAndCtx.instance.ID(), - Domain: " " + instAndCtx.customDomain, - }, - inputContext: ctxWithSysAuthZ, - }, - } + for _, tc := range tt { + t.Run(tc.testName, func(t *testing.T) { + // Test + res, err := inst.Client.InstanceV2Beta.RemoveCustomDomain(tc.inputContext, tc.inputRequest) - for _, tc := range tt { - t.Run(fmt.Sprintf("%s - %s", instAndCtx.testType, tc.testName), func(t *testing.T) { - // Test - res, err := instAndCtx.instance.Client.InstanceV2Beta.RemoveCustomDomain(tc.inputContext, tc.inputRequest) + // Verify + assert.Equal(t, tc.expectedErrorCode, status.Code(err)) + assert.Contains(t, status.Convert(err).Message(), tc.expectedErrorMsg) - // Verify - assert.Equal(t, tc.expectedErrorCode, status.Code(err)) - assert.Contains(t, status.Convert(err).Message(), tc.expectedErrorMsg) - - if tc.expectedErrorMsg == "" { - assert.NotNil(t, res) - assert.NotEmpty(t, res.GetDeletionDate()) - } - }) - } + if tc.expectedErrorMsg == "" { + assert.NotNil(t, res) + assert.NotEmpty(t, res.GetDeletionDate()) + } + }) } } @@ -246,99 +197,75 @@ func TestAddTrustedDomain(t *testing.T) { ctxWithSysAuthZ := integration.WithSystemAuthorization(ctx) - // Eventstore objects - instES := integration.NewInstance(ctxWithSysAuthZ) - orgOwnerCtxES := instES.WithAuthorization(context.Background(), integration.UserTypeOrgOwner) - d1ES := "trusted" + integration.DomainName() - - // Relational objects - instRelational := integration.NewInstance(ctxWithSysAuthZ) - integration.EnsureInstanceFeature(t, ctxWithSysAuthZ, instRelational, &feature.SetInstanceFeaturesRequest{EnableRelationalTables: gu.Ptr(true)}, func(tCollect *assert.CollectT, got *feature.GetInstanceFeaturesResponse) { - assert.True(tCollect, got.EnableRelationalTables.GetEnabled()) - }) - orgOwnerCtxRelational := instRelational.WithAuthorizationToken(context.Background(), integration.UserTypeOrgOwner) - d1Relational := "trusted" + integration.DomainName() + inst := integration.NewInstance(ctxWithSysAuthZ) + orgOwnerCtx := inst.WithAuthorization(context.Background(), integration.UserTypeOrgOwner) + d1 := "trusted" + integration.DomainName() t.Cleanup(func() { - instES.Client.InstanceV2Beta.DeleteInstance(ctxWithSysAuthZ, &instance.DeleteInstanceRequest{InstanceId: instES.ID()}) - instRelational.Client.InstanceV2Beta.DeleteInstance(ctxWithSysAuthZ, &instance.DeleteInstanceRequest{InstanceId: instRelational.ID()}) + inst.Client.InstanceV2Beta.DeleteInstance(ctxWithSysAuthZ, &instance.DeleteInstanceRequest{InstanceId: inst.ID()}) }) - type instAndCtx struct { - testType string - inst *integration.Instance - orgOwnerCtx context.Context - domain string + tt := []struct { + testName string + inputContext context.Context + inputRequest *instance.AddTrustedDomainRequest + expectedErrorMsg string + expectedErrorCode codes.Code + }{ + { + testName: "when invalid context should return unauthN error", + inputRequest: &instance.AddTrustedDomainRequest{ + InstanceId: inst.ID(), + Domain: d1, + }, + inputContext: context.Background(), + expectedErrorCode: codes.Unauthenticated, + expectedErrorMsg: "auth header missing", + }, + { + testName: "when unauthZ context should return unauthZ error", + inputRequest: &instance.AddTrustedDomainRequest{ + InstanceId: inst.ID(), + Domain: d1, + }, + inputContext: orgOwnerCtx, + expectedErrorCode: codes.PermissionDenied, + expectedErrorMsg: "No matching permissions found (AUTH-5mWD2)", + }, + { + testName: "when invalid domain should return invalid argument error", + inputRequest: &instance.AddTrustedDomainRequest{ + InstanceId: inst.ID(), + Domain: " ", + }, + inputContext: ctxWithSysAuthZ, + expectedErrorCode: codes.InvalidArgument, + expectedErrorMsg: "Errors.Invalid.Argument", + }, + { + testName: "when valid request should return successful response", + inputRequest: &instance.AddTrustedDomainRequest{ + InstanceId: inst.ID(), + Domain: " " + d1, + }, + inputContext: ctxWithSysAuthZ, + }, } - testedInstanceAndCtxs := []instAndCtx{ - {testType: "eventstore", inst: instES, orgOwnerCtx: orgOwnerCtxES, domain: d1ES}, - {testType: "relational", inst: instRelational, orgOwnerCtx: orgOwnerCtxRelational, domain: d1Relational}, - } + for _, tc := range tt { + t.Run(tc.testName, func(t *testing.T) { + // Test + res, err := inst.Client.InstanceV2Beta.AddTrustedDomain(tc.inputContext, tc.inputRequest) - for _, stateCase := range testedInstanceAndCtxs { - tt := []struct { - testName string - inputContext context.Context - inputRequest *instance.AddTrustedDomainRequest - expectedErrorMsg string - expectedErrorCode codes.Code - }{ - { - testName: "when invalid context should return unauthN error", - inputRequest: &instance.AddTrustedDomainRequest{ - InstanceId: stateCase.inst.ID(), - Domain: stateCase.domain, - }, - inputContext: context.Background(), - expectedErrorCode: codes.Unauthenticated, - expectedErrorMsg: "auth header missing", - }, - { - testName: "when unauthZ context should return unauthZ error", - inputRequest: &instance.AddTrustedDomainRequest{ - InstanceId: stateCase.inst.ID(), - Domain: stateCase.domain, - }, - inputContext: stateCase.orgOwnerCtx, - expectedErrorCode: codes.PermissionDenied, - expectedErrorMsg: "No matching permissions found (AUTH-5mWD2)", - }, - { - testName: "when invalid domain should return invalid argument error", - inputRequest: &instance.AddTrustedDomainRequest{ - InstanceId: stateCase.inst.ID(), - Domain: " ", - }, - inputContext: ctxWithSysAuthZ, - expectedErrorCode: codes.InvalidArgument, - expectedErrorMsg: "Errors.Invalid.Argument", - }, - { - testName: "when valid request should return successful response", - inputRequest: &instance.AddTrustedDomainRequest{ - InstanceId: stateCase.inst.ID(), - Domain: " " + stateCase.domain, - }, - inputContext: ctxWithSysAuthZ, - }, - } + // Verify + assert.Equal(t, tc.expectedErrorCode, status.Code(err)) + assert.Contains(t, status.Convert(err).Message(), tc.expectedErrorMsg) - for _, tc := range tt { - t.Run(fmt.Sprintf("%s - %s", stateCase.testType, tc.testName), func(t *testing.T) { - // Test - res, err := stateCase.inst.Client.InstanceV2Beta.AddTrustedDomain(tc.inputContext, tc.inputRequest) - - // Verify - assert.Equal(t, tc.expectedErrorCode, status.Code(err)) - assert.Contains(t, status.Convert(err).Message(), tc.expectedErrorMsg) - - if tc.expectedErrorMsg == "" { - assert.NotNil(t, res) - assert.NotEmpty(t, res.GetCreationDate()) - } - }) - } + if tc.expectedErrorMsg == "" { + assert.NotNil(t, res) + assert.NotEmpty(t, res.GetCreationDate()) + } + }) } } @@ -350,95 +277,68 @@ func TestRemoveTrustedDomain(t *testing.T) { defer cancel() ctxWithSysAuthZ := integration.WithSystemAuthorization(ctx) - // Eventstore objects - instES := integration.NewInstance(ctxWithSysAuthZ) - orgOwnerCtxES := instES.WithAuthorizationToken(context.Background(), integration.UserTypeOrgOwner) - trustedDomainES := integration.DomainName() - _, err := instES.Client.InstanceV2Beta.AddTrustedDomain(ctxWithSysAuthZ, &instance.AddTrustedDomainRequest{InstanceId: instES.ID(), Domain: trustedDomainES}) - require.Nil(t, err) - // Relational objects - instRelational := integration.NewInstance(ctxWithSysAuthZ) - integration.EnsureInstanceFeature(t, ctxWithSysAuthZ, instRelational, &feature.SetInstanceFeaturesRequest{EnableRelationalTables: gu.Ptr(true)}, func(tCollect *assert.CollectT, got *feature.GetInstanceFeaturesResponse) { - assert.True(tCollect, got.EnableRelationalTables.GetEnabled()) - }) - orgOwnerCtxRelational := instRelational.WithAuthorizationToken(context.Background(), integration.UserTypeOrgOwner) - trustedDomainRelational := integration.DomainName() - _, err = instRelational.Client.InstanceV2Beta.AddTrustedDomain(ctxWithSysAuthZ, &instance.AddTrustedDomainRequest{InstanceId: instRelational.ID(), Domain: trustedDomainRelational}) + inst := integration.NewInstance(ctxWithSysAuthZ) + orgOwnerCtx := inst.WithAuthorizationToken(context.Background(), integration.UserTypeOrgOwner) + trustedDomain := integration.DomainName() + _, err := inst.Client.InstanceV2Beta.AddTrustedDomain(ctxWithSysAuthZ, &instance.AddTrustedDomainRequest{InstanceId: inst.ID(), Domain: trustedDomain}) require.Nil(t, err) t.Cleanup(func() { - instES.Client.InstanceV2Beta.RemoveTrustedDomain(ctxWithSysAuthZ, &instance.RemoveTrustedDomainRequest{InstanceId: instES.ID(), Domain: trustedDomainES}) - instES.Client.InstanceV2Beta.DeleteInstance(ctxWithSysAuthZ, &instance.DeleteInstanceRequest{InstanceId: instES.ID()}) - - instRelational.Client.InstanceV2Beta.RemoveTrustedDomain(ctxWithSysAuthZ, &instance.RemoveTrustedDomainRequest{InstanceId: instRelational.ID(), Domain: trustedDomainRelational}) - instRelational.Client.InstanceV2Beta.DeleteInstance(ctxWithSysAuthZ, &instance.DeleteInstanceRequest{InstanceId: instRelational.ID()}) + inst.Client.InstanceV2Beta.RemoveTrustedDomain(ctxWithSysAuthZ, &instance.RemoveTrustedDomainRequest{InstanceId: inst.ID(), Domain: trustedDomain}) + inst.Client.InstanceV2Beta.DeleteInstance(ctxWithSysAuthZ, &instance.DeleteInstanceRequest{InstanceId: inst.ID()}) }) - type instAndCtx struct { - testType string - inst *integration.Instance - orgOwnerCtx context.Context - domain string + tt := []struct { + testName string + inputContext context.Context + inputRequest *instance.RemoveTrustedDomainRequest + expectedErrorMsg string + expectedErrorCode codes.Code + }{ + { + testName: "when invalid context should return unauthN error", + inputRequest: &instance.RemoveTrustedDomainRequest{ + InstanceId: inst.ID(), + Domain: "trusted1", + }, + inputContext: context.Background(), + expectedErrorCode: codes.Unauthenticated, + expectedErrorMsg: "auth header missing", + }, + { + testName: "when unauthZ context should return unauthZ error", + inputRequest: &instance.RemoveTrustedDomainRequest{ + InstanceId: inst.ID(), + Domain: "trusted1", + }, + inputContext: orgOwnerCtx, + expectedErrorCode: codes.PermissionDenied, + expectedErrorMsg: "No matching permissions found (AUTH-5mWD2)", + }, + { + testName: "when valid request should return successful response", + inputRequest: &instance.RemoveTrustedDomainRequest{ + InstanceId: inst.ID(), + Domain: " " + trustedDomain, + }, + inputContext: ctxWithSysAuthZ, + }, } - testedInstanceAndCtxs := []instAndCtx{ - {testType: "eventstore", inst: instES, orgOwnerCtx: orgOwnerCtxES, domain: trustedDomainES}, - {testType: "relational", inst: instRelational, orgOwnerCtx: orgOwnerCtxRelational, domain: trustedDomainRelational}, - } + for _, tc := range tt { + t.Run(tc.testName, func(t *testing.T) { + // Test + res, err := inst.Client.InstanceV2Beta.RemoveTrustedDomain(tc.inputContext, tc.inputRequest) - for _, stateCase := range testedInstanceAndCtxs { - tt := []struct { - testName string - inputContext context.Context - inputRequest *instance.RemoveTrustedDomainRequest - expectedErrorMsg string - expectedErrorCode codes.Code - }{ - { - testName: "when invalid context should return unauthN error", - inputRequest: &instance.RemoveTrustedDomainRequest{ - InstanceId: stateCase.inst.ID(), - Domain: "trusted1", - }, - inputContext: context.Background(), - expectedErrorCode: codes.Unauthenticated, - expectedErrorMsg: "auth header missing", - }, - { - testName: "when unauthZ context should return unauthZ error", - inputRequest: &instance.RemoveTrustedDomainRequest{ - InstanceId: stateCase.inst.ID(), - Domain: "trusted1", - }, - inputContext: stateCase.orgOwnerCtx, - expectedErrorCode: codes.PermissionDenied, - expectedErrorMsg: "No matching permissions found (AUTH-5mWD2)", - }, - { - testName: "when valid request should return successful response", - inputRequest: &instance.RemoveTrustedDomainRequest{ - InstanceId: stateCase.inst.ID(), - Domain: " " + stateCase.domain, - }, - inputContext: ctxWithSysAuthZ, - }, - } + // Verify + assert.Equal(t, tc.expectedErrorCode, status.Code(err)) + assert.Equal(t, tc.expectedErrorMsg, status.Convert(err).Message()) - for _, tc := range tt { - t.Run(fmt.Sprintf("%s - %s", stateCase.testType, tc.testName), func(t *testing.T) { - // Test - res, err := stateCase.inst.Client.InstanceV2Beta.RemoveTrustedDomain(tc.inputContext, tc.inputRequest) - - // Verify - assert.Equal(t, tc.expectedErrorCode, status.Code(err)) - assert.Equal(t, tc.expectedErrorMsg, status.Convert(err).Message()) - - if tc.expectedErrorMsg == "" { - require.NotNil(t, res) - require.NotEmpty(t, res.GetDeletionDate()) - } - }) - } + if tc.expectedErrorMsg == "" { + require.NotNil(t, res) + require.NotEmpty(t, res.GetDeletionDate()) + } + }) } } diff --git a/internal/api/grpc/instance/v2beta/integration_test/instance_test.go b/internal/api/grpc/instance/v2beta/integration_test/instance_test.go index 4e76eb7b1..084a4d659 100644 --- a/internal/api/grpc/instance/v2beta/integration_test/instance_test.go +++ b/internal/api/grpc/instance/v2beta/integration_test/instance_test.go @@ -4,18 +4,15 @@ package instance_test import ( "context" - "fmt" "testing" "time" - "github.com/muhlemmer/gu" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "github.com/zitadel/zitadel/internal/integration" - "github.com/zitadel/zitadel/pkg/grpc/feature/v2" instance "github.com/zitadel/zitadel/pkg/grpc/instance/v2beta" ) @@ -28,76 +25,60 @@ func TestDeleteInstace(t *testing.T) { ctxWithSysAuthZ := integration.WithSystemAuthorization(ctx) - instEventStore := integration.NewInstance(ctxWithSysAuthZ) + inst := integration.NewInstance(ctxWithSysAuthZ) - instRelational := integration.NewInstance(ctxWithSysAuthZ) - integration.EnsureInstanceFeature(t, ctxWithSysAuthZ, instRelational, &feature.SetInstanceFeaturesRequest{EnableRelationalTables: gu.Ptr(true)}, func(tCollect *assert.CollectT, got *feature.GetInstanceFeaturesResponse) { - assert.True(tCollect, got.EnableRelationalTables.GetEnabled()) - }) - - type instanceWithType struct { - instType string - instance *integration.Instance - } - instances := []instanceWithType{ - {instType: "eventstore", instance: instEventStore}, - {instType: "relational", instance: instRelational}, - } t.Cleanup(func() { - instRelational.Client.InstanceV2Beta.DeleteInstance(ctxWithSysAuthZ, &instance.DeleteInstanceRequest{InstanceId: instRelational.ID()}) - instEventStore.Client.InstanceV2Beta.DeleteInstance(ctxWithSysAuthZ, &instance.DeleteInstanceRequest{InstanceId: instEventStore.ID()}) + inst.Client.InstanceV2Beta.DeleteInstance(ctxWithSysAuthZ, &instance.DeleteInstanceRequest{InstanceId: inst.ID()}) }) - for _, inst := range instances { - tt := []struct { - testName string - inputRequest *instance.DeleteInstanceRequest - inputContext context.Context - expectedErrorMsg string - expectedErrorCode codes.Code - expectedInstanceID string - expectsNullTimestamp bool - }{ - { - testName: "when invalid context should return unauthN error", - inputRequest: &instance.DeleteInstanceRequest{ - InstanceId: " ", - }, - inputContext: context.Background(), - expectedErrorCode: codes.Unauthenticated, - expectedErrorMsg: "auth header missing", + tt := []struct { + testName string + inputRequest *instance.DeleteInstanceRequest + inputContext context.Context + expectedErrorMsg string + expectedErrorCode codes.Code + expectedInstanceID string + expectsNullTimestamp bool + }{ + { + testName: "when invalid context should return unauthN error", + inputRequest: &instance.DeleteInstanceRequest{ + InstanceId: " ", }, - { - testName: "when instance not found should return no error", - inputRequest: &instance.DeleteInstanceRequest{ - InstanceId: inst.instance.ID() + "invalid", - }, - inputContext: ctxWithSysAuthZ, - expectsNullTimestamp: true, + inputContext: context.Background(), + expectedErrorCode: codes.Unauthenticated, + expectedErrorMsg: "auth header missing", + }, + { + testName: "when instance not found should return no error", + inputRequest: &instance.DeleteInstanceRequest{ + InstanceId: inst.ID() + "invalid", }, - { - testName: "when delete succeeds should return deletion date", - inputRequest: &instance.DeleteInstanceRequest{ - InstanceId: inst.instance.ID(), - }, - inputContext: ctxWithSysAuthZ, - expectedInstanceID: inst.instance.ID(), + inputContext: ctxWithSysAuthZ, + expectsNullTimestamp: true, + }, + { + testName: "when delete succeeds should return deletion date", + inputRequest: &instance.DeleteInstanceRequest{ + InstanceId: inst.ID(), }, - } - for _, tc := range tt { - t.Run(fmt.Sprintf("%s - %s", inst.instType, tc.testName), func(t *testing.T) { - // Test - res, err := inst.instance.Client.InstanceV2Beta.DeleteInstance(tc.inputContext, tc.inputRequest) + inputContext: ctxWithSysAuthZ, + expectedInstanceID: inst.ID(), + }, + } + for _, tc := range tt { + t.Run(tc.testName, func(t *testing.T) { + // Test + res, err := inst.Client.InstanceV2Beta.DeleteInstance(tc.inputContext, tc.inputRequest) - // Verify - assert.Equal(t, tc.expectedErrorCode, status.Code(err)) - assert.Contains(t, status.Convert(err).Message(), tc.expectedErrorMsg) - if tc.expectedErrorMsg == "" { - require.NotNil(t, res) - require.Equal(t, tc.expectsNullTimestamp, res.GetDeletionDate() == nil) - } - }) - } + // Verify + assert.Equal(t, tc.expectedErrorCode, status.Code(err)) + assert.Contains(t, status.Convert(err).Message(), tc.expectedErrorMsg) + if tc.expectedErrorMsg == "" { + require.NotNil(t, res) + require.Equal(t, tc.expectsNullTimestamp, res.GetDeletionDate() == nil) + } + }) } } @@ -110,90 +91,72 @@ func TestUpdateInstace(t *testing.T) { ctxWithSysAuthZ := integration.WithSystemAuthorization(ctx) - instEventStore := integration.NewInstance(ctxWithSysAuthZ) - orgOwnerESCtx := instEventStore.WithAuthorizationToken(context.Background(), integration.UserTypeOrgOwner) - - instRelational := integration.NewInstance(ctxWithSysAuthZ) - integration.EnsureInstanceFeature(t, ctxWithSysAuthZ, instRelational, &feature.SetInstanceFeaturesRequest{EnableRelationalTables: gu.Ptr(true)}, func(tCollect *assert.CollectT, got *feature.GetInstanceFeaturesResponse) { - assert.True(tCollect, got.EnableRelationalTables.GetEnabled()) - }) - orgOwnerRelationalCtx := instRelational.WithAuthorizationToken(context.Background(), integration.UserTypeOrgOwner) - - instances := []struct { - instance *integration.Instance - ctx context.Context - }{ - {instance: instEventStore, ctx: orgOwnerESCtx}, - {instance: instRelational, ctx: orgOwnerRelationalCtx}, - } + inst := integration.NewInstance(ctxWithSysAuthZ) + orgOwnerCtx := inst.WithAuthorizationToken(context.Background(), integration.UserTypeOrgOwner) t.Cleanup(func() { - instEventStore.Client.InstanceV2Beta.DeleteInstance(ctxWithSysAuthZ, &instance.DeleteInstanceRequest{InstanceId: instEventStore.ID()}) - instRelational.Client.InstanceV2Beta.DeleteInstance(ctxWithSysAuthZ, &instance.DeleteInstanceRequest{InstanceId: instRelational.ID()}) + inst.Client.InstanceV2Beta.DeleteInstance(ctxWithSysAuthZ, &instance.DeleteInstanceRequest{InstanceId: inst.ID()}) }) - for i, inst := range instances { - tt := []struct { - testName string - inputRequest *instance.UpdateInstanceRequest - inputContext context.Context - expectedErrorMsg string - expectedErrorCode codes.Code - expectedNewName string - }{ - { - testName: "when invalid context should return unauthN error", - inputRequest: &instance.UpdateInstanceRequest{ - InstanceId: inst.instance.ID(), - InstanceName: " ", - }, - inputContext: context.Background(), - expectedErrorCode: codes.Unauthenticated, - expectedErrorMsg: "auth header missing", + tt := []struct { + testName string + inputRequest *instance.UpdateInstanceRequest + inputContext context.Context + expectedErrorMsg string + expectedErrorCode codes.Code + expectedNewName string + }{ + { + testName: "when invalid context should return unauthN error", + inputRequest: &instance.UpdateInstanceRequest{ + InstanceId: inst.ID(), + InstanceName: " ", }, - { - testName: "when unauthZ context should return unauthZ error", - inputRequest: &instance.UpdateInstanceRequest{ - InstanceId: inst.instance.ID(), - InstanceName: " ", - }, - inputContext: inst.ctx, - expectedErrorCode: codes.PermissionDenied, - expectedErrorMsg: "No matching permissions found (AUTH-5mWD2)", + inputContext: context.Background(), + expectedErrorCode: codes.Unauthenticated, + expectedErrorMsg: "auth header missing", + }, + { + testName: "when unauthZ context should return unauthZ error", + inputRequest: &instance.UpdateInstanceRequest{ + InstanceId: inst.ID(), + InstanceName: " ", }, - { - testName: "when update succeeds should change instance name", - inputRequest: &instance.UpdateInstanceRequest{ - InstanceId: inst.instance.ID(), - InstanceName: fmt.Sprintf("an-updated-name %d", i), - }, - inputContext: ctxWithSysAuthZ, - expectedNewName: fmt.Sprintf("an-updated-name %d", i), + inputContext: orgOwnerCtx, + expectedErrorCode: codes.PermissionDenied, + expectedErrorMsg: "No matching permissions found (AUTH-5mWD2)", + }, + { + testName: "when update succeeds should change instance name", + inputRequest: &instance.UpdateInstanceRequest{ + InstanceId: inst.ID(), + InstanceName: "an-updated-name", }, - } + inputContext: ctxWithSysAuthZ, + expectedNewName: "an-updated-name", + }, + } - for _, tc := range tt { - t.Run(tc.testName, func(t *testing.T) { - // Test - res, err := inst.instance.Client.InstanceV2Beta.UpdateInstance(tc.inputContext, tc.inputRequest) + for _, tc := range tt { + t.Run(tc.testName, func(t *testing.T) { + // Test + res, err := inst.Client.InstanceV2Beta.UpdateInstance(tc.inputContext, tc.inputRequest) - // Verify - assert.Equal(t, tc.expectedErrorCode, status.Code(err)) - assert.Equal(t, tc.expectedErrorMsg, status.Convert(err).Message()) - if tc.expectedErrorMsg == "" { + // Verify + assert.Equal(t, tc.expectedErrorCode, status.Code(err)) + assert.Equal(t, tc.expectedErrorMsg, status.Convert(err).Message()) + if tc.expectedErrorMsg == "" { - require.NotNil(t, res) - assert.NotEmpty(t, res.GetChangeDate()) - - retryDuration, tick := integration.WaitForAndTickWithMaxDuration(tc.inputContext, time.Minute) - require.EventuallyWithT(t, func(tt *assert.CollectT) { - retrievedInstance, err := inst.instance.Client.InstanceV2Beta.GetInstance(tc.inputContext, &instance.GetInstanceRequest{InstanceId: inst.instance.ID()}) - require.Nil(tt, err) - assert.Equal(tt, tc.expectedNewName, retrievedInstance.GetInstance().GetName()) - }, retryDuration, tick, "timeout waiting for expected execution result") - } - }) - } + require.NotNil(t, res) + assert.NotEmpty(t, res.GetChangeDate()) + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(tc.inputContext, time.Minute) + require.EventuallyWithT(t, func(tt *assert.CollectT) { + retrievedInstance, err := inst.Client.InstanceV2Beta.GetInstance(tc.inputContext, &instance.GetInstanceRequest{InstanceId: inst.ID()}) + require.Nil(tt, err) + assert.Equal(tt, tc.expectedNewName, retrievedInstance.GetInstance().GetName()) + }, retryDuration, tick, "timeout waiting for expected execution result") + } + }) } } diff --git a/internal/api/grpc/instance/v2beta/integration_test/query_test.go b/internal/api/grpc/instance/v2beta/integration_test/query_test.go index f777ade4d..4bfaafcb1 100644 --- a/internal/api/grpc/instance/v2beta/integration_test/query_test.go +++ b/internal/api/grpc/instance/v2beta/integration_test/query_test.go @@ -4,11 +4,9 @@ package instance_test import ( "context" - "fmt" "testing" "time" - "github.com/muhlemmer/gu" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "google.golang.org/grpc/codes" @@ -29,76 +27,43 @@ func TestGetInstance(t *testing.T) { defer cancel() ctxWithSysAuthZ := integration.WithSystemAuthorization(ctx) - instEventStore := integration.NewInstance(ctxWithSysAuthZ) - orgOwnerCtxES := instEventStore.WithAuthorizationToken(context.Background(), integration.UserTypeOrgOwner) - - instRelational := integration.NewInstance(ctxWithSysAuthZ) - orgOwnerCtxREL := instRelational.WithAuthorizationToken(context.Background(), integration.UserTypeOrgOwner) - integration.EnsureInstanceFeature(t, ctxWithSysAuthZ, instRelational, &feature.SetInstanceFeaturesRequest{EnableRelationalTables: gu.Ptr(true)}, func(tCollect *assert.CollectT, got *feature.GetInstanceFeaturesResponse) { - assert.True(tCollect, got.GetEnableRelationalTables().GetEnabled()) - }) + inst := integration.NewInstance(ctxWithSysAuthZ) + orgOwnerCtx := inst.WithAuthorizationToken(context.Background(), integration.UserTypeOrgOwner) t.Cleanup(func() { - instRelational.Client.InstanceV2Beta.DeleteInstance(ctxWithSysAuthZ, &instance.DeleteInstanceRequest{InstanceId: instRelational.ID()}) - instEventStore.Client.InstanceV2Beta.DeleteInstance(ctxWithSysAuthZ, &instance.DeleteInstanceRequest{InstanceId: instEventStore.ID()}) + inst.Client.InstanceV2Beta.DeleteInstance(ctxWithSysAuthZ, &instance.DeleteInstanceRequest{InstanceId: inst.ID()}) }) tt := []struct { testName string - instance *integration.Instance inputContext context.Context expectedInstanceID string expectedErrorMsg string expectedErrorCode codes.Code }{ { - testName: "eventstore instance - when unauthN context should return unauthN error", + testName: "when unauthN context should return unauthN error", inputContext: context.Background(), - instance: instEventStore, expectedErrorCode: codes.Unauthenticated, expectedErrorMsg: "auth header missing", }, { - testName: "eventstore instance - when unauthZ context should return unauthZ error", - inputContext: orgOwnerCtxES, - instance: instEventStore, + testName: "when unauthZ context should return unauthZ error", + inputContext: orgOwnerCtx, expectedErrorCode: codes.PermissionDenied, expectedErrorMsg: "No matching permissions found (AUTH-5mWD2)", }, { - testName: "eventstore instance - when request succeeds should return matching instance", + testName: "when request succeeds should return matching instance", inputContext: ctxWithSysAuthZ, - instance: instEventStore, - expectedInstanceID: instEventStore.ID(), - }, - - // Relational - { - testName: "relational instance - when unauthN context should return unauthN error", - inputContext: context.Background(), - instance: instRelational, - expectedErrorCode: codes.Unauthenticated, - expectedErrorMsg: "auth header missing", - }, - { - testName: "relational instance - when unauthZ context should return unauthZ error", - inputContext: orgOwnerCtxREL, - instance: instRelational, - expectedErrorCode: codes.PermissionDenied, - expectedErrorMsg: "No matching permissions found (AUTH-5mWD2)", - }, - { - testName: "relational instance - when request succeeds should return matching instance", - inputContext: ctxWithSysAuthZ, - instance: instRelational, - expectedInstanceID: instRelational.ID(), + expectedInstanceID: inst.ID(), }, } for _, tc := range tt { t.Run(tc.testName, func(t *testing.T) { // Test - res, err := tc.instance.Client.InstanceV2Beta.GetInstance(tc.inputContext, &instance.GetInstanceRequest{InstanceId: tc.instance.ID()}) + res, err := inst.Client.InstanceV2Beta.GetInstance(tc.inputContext, &instance.GetInstanceRequest{InstanceId: inst.ID()}) // Verify assert.Equal(t, tc.expectedErrorCode, status.Code(err)) @@ -140,8 +105,6 @@ func TestListInstances(t *testing.T) { orgOwnerCtx := inst.WithAuthorizationToken(context.Background(), integration.UserTypeOrgOwner) - relTableState := integration.RelationalTablesEnableMatrix(t, ctx, ctxWithSysAuthZ) - tt := []struct { testName string inputRequest *instance.ListInstancesRequest @@ -195,29 +158,26 @@ func TestListInstances(t *testing.T) { }, } - for _, stateCase := range relTableState { - for _, tc := range tt { - t.Run(fmt.Sprintf("%s - %s", stateCase.Name, tc.testName), func(t *testing.T) { - // Test - res, err := inst.Client.InstanceV2Beta.ListInstances(tc.inputContext, tc.inputRequest) + for _, tc := range tt { + t.Run(tc.testName, func(t *testing.T) { + // Test + res, err := inst.Client.InstanceV2Beta.ListInstances(tc.inputContext, tc.inputRequest) - // Verify - assert.Equal(t, tc.expectedErrorCode, status.Code(err)) - assert.Equal(t, tc.expectedErrorMsg, status.Convert(err).Message()) + // Verify + assert.Equal(t, tc.expectedErrorCode, status.Code(err)) + assert.Equal(t, tc.expectedErrorMsg, status.Convert(err).Message()) - if tc.expectedErrorMsg == "" { - require.NotNil(t, res) + if tc.expectedErrorMsg == "" { + require.NotNil(t, res) - require.Len(t, res.GetInstances(), len(tc.expectedInstances)) + require.Len(t, res.GetInstances(), len(tc.expectedInstances)) - for i, ins := range res.GetInstances() { - assert.Equal(t, tc.expectedInstances[i], ins.GetId()) - } + for i, ins := range res.GetInstances() { + assert.Equal(t, tc.expectedInstances[i], ins.GetId()) } - }) - } + } + }) } - } func TestListCustomDomains(t *testing.T) { @@ -229,125 +189,98 @@ func TestListCustomDomains(t *testing.T) { ctxWithSysAuthZ := integration.WithSystemAuthorization(ctx) - // Eventstore objects - instES := integration.NewInstance(ctxWithSysAuthZ) - orgOwnerCtxES := instES.WithAuthorization(context.Background(), integration.UserTypeOrgOwner) - d1ES, d2ES := "custom."+integration.DomainName(), "custom."+integration.DomainName() - _, err := instES.Client.InstanceV2Beta.AddCustomDomain(ctxWithSysAuthZ, &instance.AddCustomDomainRequest{InstanceId: instES.ID(), Domain: d1ES}) + inst := integration.NewInstance(ctxWithSysAuthZ) + orgOwnerCtx := inst.WithAuthorization(context.Background(), integration.UserTypeOrgOwner) + d1, d2 := "custom."+integration.DomainName(), "custom."+integration.DomainName() + _, err := inst.Client.InstanceV2Beta.AddCustomDomain(ctxWithSysAuthZ, &instance.AddCustomDomainRequest{InstanceId: inst.ID(), Domain: d1}) require.Nil(t, err) - _, err = instES.Client.InstanceV2Beta.AddCustomDomain(ctxWithSysAuthZ, &instance.AddCustomDomainRequest{InstanceId: instES.ID(), Domain: d2ES}) - require.Nil(t, err) - - // Relational objects - instRelational := integration.NewInstance(ctxWithSysAuthZ) - integration.EnsureInstanceFeature(t, ctx, instRelational, &feature.SetInstanceFeaturesRequest{EnableRelationalTables: gu.Ptr(true)}, func(tCollect *assert.CollectT, got *feature.GetInstanceFeaturesResponse) { - assert.True(tCollect, got.EnableRelationalTables.GetEnabled()) - }) - orgOwnerCtxRelational := instRelational.WithAuthorization(context.Background(), integration.UserTypeOrgOwner) - d1Relational, d2Relational := "custom."+integration.DomainName(), "custom."+integration.DomainName() - _, err = instRelational.Client.InstanceV2Beta.AddCustomDomain(ctxWithSysAuthZ, &instance.AddCustomDomainRequest{InstanceId: instRelational.ID(), Domain: d1Relational}) - require.Nil(t, err) - _, err = instRelational.Client.InstanceV2Beta.AddCustomDomain(ctxWithSysAuthZ, &instance.AddCustomDomainRequest{InstanceId: instRelational.ID(), Domain: d2Relational}) + _, err = inst.Client.InstanceV2Beta.AddCustomDomain(ctxWithSysAuthZ, &instance.AddCustomDomainRequest{InstanceId: inst.ID(), Domain: d2}) require.Nil(t, err) t.Cleanup(func() { - instES.Client.InstanceV2Beta.RemoveCustomDomain(ctxWithSysAuthZ, &instance.RemoveCustomDomainRequest{InstanceId: instES.ID(), Domain: d1ES}) - instES.Client.InstanceV2Beta.RemoveCustomDomain(ctxWithSysAuthZ, &instance.RemoveCustomDomainRequest{InstanceId: instES.ID(), Domain: d2ES}) - instES.Client.InstanceV2Beta.DeleteInstance(ctxWithSysAuthZ, &instance.DeleteInstanceRequest{InstanceId: instES.ID()}) - - instRelational.Client.InstanceV2Beta.RemoveCustomDomain(ctxWithSysAuthZ, &instance.RemoveCustomDomainRequest{InstanceId: instRelational.ID(), Domain: d1Relational}) - instRelational.Client.InstanceV2Beta.RemoveCustomDomain(ctxWithSysAuthZ, &instance.RemoveCustomDomainRequest{InstanceId: instRelational.ID(), Domain: d2Relational}) - instRelational.Client.InstanceV2Beta.DeleteInstance(ctxWithSysAuthZ, &instance.DeleteInstanceRequest{InstanceId: instRelational.ID()}) + inst.Client.InstanceV2Beta.RemoveCustomDomain(ctxWithSysAuthZ, &instance.RemoveCustomDomainRequest{InstanceId: inst.ID(), Domain: d1}) + inst.Client.InstanceV2Beta.RemoveCustomDomain(ctxWithSysAuthZ, &instance.RemoveCustomDomainRequest{InstanceId: inst.ID(), Domain: d2}) + inst.Client.InstanceV2Beta.DeleteInstance(ctxWithSysAuthZ, &instance.DeleteInstanceRequest{InstanceId: inst.ID()}) }) - testData := []struct { - testType string - inst *integration.Instance - orgOwnerCtx context.Context - expectedDomains []string + expectedDomains := []string{d1, d2} + + tt := []struct { + testName string + inputRequest *instance.ListCustomDomainsRequest + inputContext context.Context + expectedErrorMsg string + expectedErrorCode codes.Code + expectedDomains []string }{ - {testType: "eventstore", inst: instES, orgOwnerCtx: orgOwnerCtxES, expectedDomains: []string{d1ES, d2ES}}, - {testType: "relational", inst: instRelational, orgOwnerCtx: orgOwnerCtxRelational, expectedDomains: []string{d1Relational, d2Relational}}, + { + testName: "when invalid context should return unauthN error", + inputRequest: &instance.ListCustomDomainsRequest{ + InstanceId: inst.ID(), + Pagination: &filter.PaginationRequest{Offset: 0, Limit: 10}, + }, + inputContext: context.Background(), + expectedErrorCode: codes.Unauthenticated, + expectedErrorMsg: "auth header missing", + }, + { + testName: "when unauthZ context should return unauthZ error", + inputRequest: &instance.ListCustomDomainsRequest{ + InstanceId: inst.ID(), + Pagination: &filter.PaginationRequest{Offset: 0, Limit: 10}, + SortingColumn: instance.DomainFieldName_DOMAIN_FIELD_NAME_CREATION_DATE, + Queries: []*instance.DomainSearchQuery{ + { + Query: &instance.DomainSearchQuery_DomainQuery{ + DomainQuery: &instance.DomainQuery{Domain: "custom", Method: object.TextQueryMethod_TEXT_QUERY_METHOD_CONTAINS}, + }, + }, + }, + }, + inputContext: orgOwnerCtx, + expectedErrorCode: codes.PermissionDenied, + expectedErrorMsg: "No matching permissions found (AUTH-5mWD2)", + }, + { + testName: "when valid request with filter should return paginated response", + inputRequest: &instance.ListCustomDomainsRequest{ + InstanceId: inst.ID(), + Pagination: &filter.PaginationRequest{Offset: 0, Limit: 10}, + SortingColumn: instance.DomainFieldName_DOMAIN_FIELD_NAME_CREATION_DATE, + Queries: []*instance.DomainSearchQuery{ + { + Query: &instance.DomainSearchQuery_DomainQuery{ + DomainQuery: &instance.DomainQuery{Domain: "custom", Method: object.TextQueryMethod_TEXT_QUERY_METHOD_CONTAINS}, + }, + }, + }, + }, + inputContext: ctxWithSysAuthZ, + expectedDomains: expectedDomains, + }, } - for _, td := range testData { - tt := []struct { - testName string - inputRequest *instance.ListCustomDomainsRequest - inputContext context.Context - expectedErrorMsg string - expectedErrorCode codes.Code - expectedDomains []string - }{ - { - testName: "when invalid context should return unauthN error", - inputRequest: &instance.ListCustomDomainsRequest{ - InstanceId: td.inst.ID(), - Pagination: &filter.PaginationRequest{Offset: 0, Limit: 10}, - }, - inputContext: context.Background(), - expectedErrorCode: codes.Unauthenticated, - expectedErrorMsg: "auth header missing", - }, - { - testName: "when unauthZ context should return unauthZ error", - inputRequest: &instance.ListCustomDomainsRequest{ - InstanceId: td.inst.ID(), - Pagination: &filter.PaginationRequest{Offset: 0, Limit: 10}, - SortingColumn: instance.DomainFieldName_DOMAIN_FIELD_NAME_CREATION_DATE, - Queries: []*instance.DomainSearchQuery{ - { - Query: &instance.DomainSearchQuery_DomainQuery{ - DomainQuery: &instance.DomainQuery{Domain: "custom", Method: object.TextQueryMethod_TEXT_QUERY_METHOD_CONTAINS}, - }, - }, - }, - }, - inputContext: td.orgOwnerCtx, - expectedErrorCode: codes.PermissionDenied, - expectedErrorMsg: "No matching permissions found (AUTH-5mWD2)", - }, - { - testName: "when valid request with filter should return paginated response", - inputRequest: &instance.ListCustomDomainsRequest{ - InstanceId: td.inst.ID(), - Pagination: &filter.PaginationRequest{Offset: 0, Limit: 10}, - SortingColumn: instance.DomainFieldName_DOMAIN_FIELD_NAME_CREATION_DATE, - Queries: []*instance.DomainSearchQuery{ - { - Query: &instance.DomainSearchQuery_DomainQuery{ - DomainQuery: &instance.DomainQuery{Domain: "custom", Method: object.TextQueryMethod_TEXT_QUERY_METHOD_CONTAINS}, - }, - }, - }, - }, - inputContext: ctxWithSysAuthZ, - expectedDomains: td.expectedDomains, - }, - } + for _, tc := range tt { + t.Run(tc.testName, func(t *testing.T) { + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(tc.inputContext, time.Minute) + require.EventuallyWithT(t, func(collect *assert.CollectT) { + // Test + res, err := inst.Client.InstanceV2Beta.ListCustomDomains(tc.inputContext, tc.inputRequest) - for _, tc := range tt { - t.Run(fmt.Sprintf("%s - %s", td.testType, tc.testName), func(t *testing.T) { - retryDuration, tick := integration.WaitForAndTickWithMaxDuration(tc.inputContext, time.Minute) - require.EventuallyWithT(t, func(collect *assert.CollectT) { - // Test - res, err := td.inst.Client.InstanceV2Beta.ListCustomDomains(tc.inputContext, tc.inputRequest) + // Verify + assert.Equal(collect, tc.expectedErrorCode, status.Code(err)) + assert.Equal(collect, tc.expectedErrorMsg, status.Convert(err).Message()) - // Verify - assert.Equal(collect, tc.expectedErrorCode, status.Code(err)) - assert.Equal(collect, tc.expectedErrorMsg, status.Convert(err).Message()) - - if tc.expectedErrorMsg == "" { - domains := []string{} - for _, d := range res.GetDomains() { - domains = append(domains, d.GetDomain()) - } - - assert.Subset(collect, domains, tc.expectedDomains) + if tc.expectedErrorMsg == "" { + domains := []string{} + for _, d := range res.GetDomains() { + domains = append(domains, d.GetDomain()) } - }, retryDuration, tick) - }) - } + + assert.Subset(collect, domains, tc.expectedDomains) + } + }, retryDuration, tick) + }) } } @@ -360,117 +293,91 @@ func TestListTrustedDomains(t *testing.T) { ctxWithSysAuthZ := integration.WithSystemAuthorization(ctx) - // Eventstore objects - instES := integration.NewInstance(ctxWithSysAuthZ) - orgOwnerCtxES := instES.WithAuthorization(context.Background(), integration.UserTypeOrgOwner) - d1ES, d2ES := "trusted."+integration.DomainName(), "trusted."+integration.DomainName() - _, err := instES.Client.InstanceV2Beta.AddTrustedDomain(ctxWithSysAuthZ, &instance.AddTrustedDomainRequest{InstanceId: instES.ID(), Domain: d1ES}) + inst := integration.NewInstance(ctxWithSysAuthZ) + orgOwnerCtx := inst.WithAuthorization(context.Background(), integration.UserTypeOrgOwner) + d1, d2 := "trusted."+integration.DomainName(), "trusted."+integration.DomainName() + _, err := inst.Client.InstanceV2Beta.AddTrustedDomain(ctxWithSysAuthZ, &instance.AddTrustedDomainRequest{InstanceId: inst.ID(), Domain: d1}) require.Nil(t, err) - _, err = instES.Client.InstanceV2Beta.AddTrustedDomain(ctxWithSysAuthZ, &instance.AddTrustedDomainRequest{InstanceId: instES.ID(), Domain: d2ES}) - require.Nil(t, err) - - // Relational objects - instRelational := integration.NewInstance(ctxWithSysAuthZ) - integration.EnsureInstanceFeature(t, ctx, instRelational, &feature.SetInstanceFeaturesRequest{EnableRelationalTables: gu.Ptr(true)}, func(tCollect *assert.CollectT, got *feature.GetInstanceFeaturesResponse) { - assert.True(tCollect, got.EnableRelationalTables.GetEnabled()) - }) - orgOwnerCtxRelational := instRelational.WithAuthorization(context.Background(), integration.UserTypeOrgOwner) - d1Relational, d2Relational := "trusted."+integration.DomainName(), "trusted."+integration.DomainName() - _, err = instRelational.Client.InstanceV2Beta.AddTrustedDomain(ctxWithSysAuthZ, &instance.AddTrustedDomainRequest{InstanceId: instRelational.ID(), Domain: d1Relational}) - require.Nil(t, err) - _, err = instRelational.Client.InstanceV2Beta.AddTrustedDomain(ctxWithSysAuthZ, &instance.AddTrustedDomainRequest{InstanceId: instRelational.ID(), Domain: d2Relational}) + _, err = inst.Client.InstanceV2Beta.AddTrustedDomain(ctxWithSysAuthZ, &instance.AddTrustedDomainRequest{InstanceId: inst.ID(), Domain: d2}) require.Nil(t, err) t.Cleanup(func() { - instES.Client.InstanceV2Beta.RemoveTrustedDomain(ctxWithSysAuthZ, &instance.RemoveTrustedDomainRequest{InstanceId: instES.ID(), Domain: d1ES}) - instES.Client.InstanceV2Beta.RemoveTrustedDomain(ctxWithSysAuthZ, &instance.RemoveTrustedDomainRequest{InstanceId: instES.ID(), Domain: d2ES}) - instES.Client.InstanceV2Beta.DeleteInstance(ctxWithSysAuthZ, &instance.DeleteInstanceRequest{InstanceId: instES.ID()}) - - instRelational.Client.InstanceV2Beta.RemoveTrustedDomain(ctxWithSysAuthZ, &instance.RemoveTrustedDomainRequest{InstanceId: instRelational.ID(), Domain: d1Relational}) - instRelational.Client.InstanceV2Beta.RemoveTrustedDomain(ctxWithSysAuthZ, &instance.RemoveTrustedDomainRequest{InstanceId: instRelational.ID(), Domain: d2Relational}) - instRelational.Client.InstanceV2Beta.DeleteInstance(ctxWithSysAuthZ, &instance.DeleteInstanceRequest{InstanceId: instRelational.ID()}) + inst.Client.InstanceV2Beta.RemoveTrustedDomain(ctxWithSysAuthZ, &instance.RemoveTrustedDomainRequest{InstanceId: inst.ID(), Domain: d1}) + inst.Client.InstanceV2Beta.RemoveTrustedDomain(ctxWithSysAuthZ, &instance.RemoveTrustedDomainRequest{InstanceId: inst.ID(), Domain: d2}) + inst.Client.InstanceV2Beta.DeleteInstance(ctxWithSysAuthZ, &instance.DeleteInstanceRequest{InstanceId: inst.ID()}) }) - testData := []struct { - testType string - inst *integration.Instance - orgOwnerCtx context.Context - expectedDomains []string + expectedDomains := []string{d1, d2} + + tt := []struct { + testName string + inputRequest *instance.ListTrustedDomainsRequest + inputContext context.Context + expectedErrorMsg string + expectedErrorCode codes.Code + expectedDomains []string }{ - {testType: "eventstore", inst: instES, orgOwnerCtx: orgOwnerCtxES, expectedDomains: []string{d1ES, d2ES}}, - {testType: "relational", inst: instRelational, orgOwnerCtx: orgOwnerCtxRelational, expectedDomains: []string{d1Relational, d2Relational}}, - } - for _, td := range testData { - tt := []struct { - testName string - inputRequest *instance.ListTrustedDomainsRequest - inputContext context.Context - expectedErrorMsg string - expectedErrorCode codes.Code - expectedDomains []string - }{ - { - testName: "when invalid context should return unauthN error", - inputRequest: &instance.ListTrustedDomainsRequest{ - InstanceId: td.inst.ID(), - Pagination: &filter.PaginationRequest{Offset: 0, Limit: 10}, - }, - inputContext: context.Background(), - expectedErrorCode: codes.Unauthenticated, - expectedErrorMsg: "auth header missing", + { + testName: "when invalid context should return unauthN error", + inputRequest: &instance.ListTrustedDomainsRequest{ + InstanceId: inst.ID(), + Pagination: &filter.PaginationRequest{Offset: 0, Limit: 10}, }, - { - testName: "when unauthZ context should return unauthZ error", - inputRequest: &instance.ListTrustedDomainsRequest{ - InstanceId: td.inst.ID(), - Pagination: &filter.PaginationRequest{Offset: 0, Limit: 10}, - }, - inputContext: td.orgOwnerCtx, - expectedErrorCode: codes.PermissionDenied, - expectedErrorMsg: "No matching permissions found (AUTH-5mWD2)", + inputContext: context.Background(), + expectedErrorCode: codes.Unauthenticated, + expectedErrorMsg: "auth header missing", + }, + { + testName: "when unauthZ context should return unauthZ error", + inputRequest: &instance.ListTrustedDomainsRequest{ + InstanceId: inst.ID(), + Pagination: &filter.PaginationRequest{Offset: 0, Limit: 10}, }, - { - testName: "when valid request with filter should return paginated response", - inputRequest: &instance.ListTrustedDomainsRequest{ - InstanceId: td.inst.ID(), - Pagination: &filter.PaginationRequest{Offset: 0, Limit: 10}, - SortingColumn: instance.TrustedDomainFieldName_TRUSTED_DOMAIN_FIELD_NAME_CREATION_DATE, - Queries: []*instance.TrustedDomainSearchQuery{ - { - Query: &instance.TrustedDomainSearchQuery_DomainQuery{ - DomainQuery: &instance.DomainQuery{Domain: "trusted", Method: object.TextQueryMethod_TEXT_QUERY_METHOD_CONTAINS}, - }, + inputContext: orgOwnerCtx, + expectedErrorCode: codes.PermissionDenied, + expectedErrorMsg: "No matching permissions found (AUTH-5mWD2)", + }, + { + testName: "when valid request with filter should return paginated response", + inputRequest: &instance.ListTrustedDomainsRequest{ + InstanceId: inst.ID(), + Pagination: &filter.PaginationRequest{Offset: 0, Limit: 10}, + SortingColumn: instance.TrustedDomainFieldName_TRUSTED_DOMAIN_FIELD_NAME_CREATION_DATE, + Queries: []*instance.TrustedDomainSearchQuery{ + { + Query: &instance.TrustedDomainSearchQuery_DomainQuery{ + DomainQuery: &instance.DomainQuery{Domain: "trusted", Method: object.TextQueryMethod_TEXT_QUERY_METHOD_CONTAINS}, }, }, }, - inputContext: ctxWithSysAuthZ, - expectedDomains: td.expectedDomains, }, - } + inputContext: ctxWithSysAuthZ, + expectedDomains: expectedDomains, + }, + } - for _, tc := range tt { - t.Run(fmt.Sprintf("%s - %s", td.testType, tc.testName), func(t *testing.T) { - retryDuration, tick := integration.WaitForAndTickWithMaxDuration(tc.inputContext, time.Minute) - require.EventuallyWithT(t, func(collect *assert.CollectT) { - // Test - res, err := td.inst.Client.InstanceV2Beta.ListTrustedDomains(tc.inputContext, tc.inputRequest) + for _, tc := range tt { + t.Run(tc.testName, func(t *testing.T) { + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(tc.inputContext, time.Minute) + require.EventuallyWithT(t, func(collect *assert.CollectT) { + // Test + res, err := inst.Client.InstanceV2Beta.ListTrustedDomains(tc.inputContext, tc.inputRequest) - // Verify - assert.Equal(collect, tc.expectedErrorCode, status.Code(err)) - assert.Equal(collect, tc.expectedErrorMsg, status.Convert(err).Message()) + // Verify + assert.Equal(collect, tc.expectedErrorCode, status.Code(err)) + assert.Equal(collect, tc.expectedErrorMsg, status.Convert(err).Message()) - if tc.expectedErrorMsg == "" { - require.NotNil(t, res) + if tc.expectedErrorMsg == "" { + require.NotNil(t, res) - domains := []string{} - for _, d := range res.GetTrustedDomain() { - domains = append(domains, d.GetDomain()) - } - - assert.Subset(collect, domains, tc.expectedDomains) + domains := []string{} + for _, d := range res.GetTrustedDomain() { + domains = append(domains, d.GetDomain()) } - }, retryDuration, tick) - }) - } + + assert.Subset(collect, domains, tc.expectedDomains) + } + }, retryDuration, tick) + }) } } diff --git a/internal/api/grpc/org/v2/integration_test/query_test.go b/internal/api/grpc/org/v2/integration_test/query_test.go index e2dad4a14..e2573835f 100644 --- a/internal/api/grpc/org/v2/integration_test/query_test.go +++ b/internal/api/grpc/org/v2/integration_test/query_test.go @@ -19,13 +19,6 @@ import ( "github.com/zitadel/zitadel/pkg/grpc/org/v2" ) -// TODO(IAM-Marco): When permission checks will be implemented, this test needs to be updated to -// add the feature flag switch: -// -// relTableState := integration.RelationalTablesEnableMatrix() -// -// See TestServer_ListOrganizations in org/v2beta/integration_test -// See https://github.com/zitadel/zitadel/issues/10219 func TestServer_ListOrganizations(t *testing.T) { ListOrgIinstance := integration.NewInstance(CTX) listOrgIAmOwnerCtx := ListOrgIinstance.WithAuthorizationToken(CTX, integration.UserTypeIAMOwner) diff --git a/internal/api/grpc/org/v2beta/integration_test/org_test.go b/internal/api/grpc/org/v2beta/integration_test/org_test.go index eb1576cd3..62b62a839 100644 --- a/internal/api/grpc/org/v2beta/integration_test/org_test.go +++ b/internal/api/grpc/org/v2beta/integration_test/org_test.go @@ -306,91 +306,84 @@ func TestServer_UpdateOrganization(t *testing.T) { sysAuthZ := integration.WithSystemAuthorization(ctx) - for _, relTableCase := range integration.RelationalTablesEnableMatrix(t, ctx, sysAuthZ) { - t.Run(relTableCase.Name, func(tt *testing.T) { - inst := relTableCase.Inst - client := inst.Client.OrgV2beta - instOwner := relTableCase.InstOwner + inst := integration.NewInstance(sysAuthZ) + client := inst.Client.OrgV2beta + instOwner := inst.WithAuthorizationToken(ctx, integration.UserTypeIAMOwner) - tt.Cleanup(func() { - _, err := inst.Client.InstanceV2.DeleteInstance(sysAuthZ, &instance.DeleteInstanceRequest{InstanceId: inst.ID()}) - assert.NoError(tt, err) - }) + t.Cleanup(func() { + _, err := inst.Client.InstanceV2.DeleteInstance(integration.WithSystemAuthorization(context.Background()), &instance.DeleteInstanceRequest{InstanceId: inst.ID()}) + assert.NoError(t, err) + }) - tt.Run("happy/new name", func(ttt *testing.T) { - // given - organisation, orgName, _ := createOrg(sysAuthZ, ttt, client) + t.Run("happy/new name", func(ttt *testing.T) { + // given + organisation, orgName, _ := createOrg(sysAuthZ, ttt, client) - // when - _, err := client.UpdateOrganization(instOwner, &v2beta_org.UpdateOrganizationRequest{ - Id: organisation.GetId(), - Name: "Not " + orgName, - }) - - // then - assert.NoError(ttt, err) - }) - - tt.Run("unhappy: same name", func(ttt *testing.T) { - // given - organisation, orgName, _ := createOrg(sysAuthZ, ttt, client) - - // when - _, err := client.UpdateOrganization(instOwner, &v2beta_org.UpdateOrganizationRequest{ - Id: organisation.GetId(), - Name: orgName, - }) - - // then - assert.Error(ttt, err) - }) - - tt.Run("unhappy: non existent org id", func(ttt *testing.T) { - // when - _, err := client.UpdateOrganization(instOwner, &v2beta_org.UpdateOrganizationRequest{ - Id: "non existing org id", - Name: "new name", - }) - - // then - assert.Error(ttt, err) - }) - - tt.Run("unhappy: no org id", func(ttt *testing.T) { - // given - createOrg(sysAuthZ, ttt, client) - - // when - _, err := client.UpdateOrganization(instOwner, &v2beta_org.UpdateOrganizationRequest{ - Id: " ", - Name: "new name", - }) - - // then - assert.Error(ttt, err) - }) - - tt.Run("unhappy: no permission", func(ttt *testing.T) { - // given - organisation, _, _ := createOrg(sysAuthZ, ttt, client) - - // when - _, err := client.UpdateOrganization( - inst.WithAuthorizationToken(ctx, integration.UserTypeOrgOwner), - &v2beta_org.UpdateOrganizationRequest{ - Id: organisation.Id, - Name: integration.OrganizationName(), - }, - ) - - // then - if relTableCase.Enabled { - ttt.Skip("TODO: remove when permission model is implemented in relational tables") - } - assert.Error(ttt, err) - }) + // when + _, err := client.UpdateOrganization(instOwner, &v2beta_org.UpdateOrganizationRequest{ + Id: organisation.GetId(), + Name: "Not " + orgName, }) - } + + // then + assert.NoError(ttt, err) + }) + + t.Run("unhappy: same name", func(ttt *testing.T) { + // given + organisation, orgName, _ := createOrg(sysAuthZ, ttt, client) + + // when + _, err := client.UpdateOrganization(instOwner, &v2beta_org.UpdateOrganizationRequest{ + Id: organisation.GetId(), + Name: orgName, + }) + + // then + assert.Error(ttt, err) + }) + + t.Run("unhappy: non existent org id", func(ttt *testing.T) { + // when + _, err := client.UpdateOrganization(instOwner, &v2beta_org.UpdateOrganizationRequest{ + Id: "non existing org id", + Name: "new name", + }) + + // then + assert.Error(ttt, err) + }) + + t.Run("unhappy: no org id", func(ttt *testing.T) { + // given + createOrg(sysAuthZ, ttt, client) + + // when + _, err := client.UpdateOrganization(instOwner, &v2beta_org.UpdateOrganizationRequest{ + Id: " ", + Name: "new name", + }) + + // then + assert.Error(ttt, err) + }) + + t.Run("unhappy: no permission", func(ttt *testing.T) { + // given + organisation, _, _ := createOrg(sysAuthZ, ttt, client) + + // when + _, err := client.UpdateOrganization( + inst.WithAuthorizationToken(ctx, integration.UserTypeOrgOwner), + &v2beta_org.UpdateOrganizationRequest{ + Id: organisation.Id, + Name: integration.OrganizationName(), + }, + ) + + // then + assert.Error(ttt, err) + }) } func TestServer_ListOrganizations(t *testing.T) { @@ -401,295 +394,290 @@ func TestServer_ListOrganizations(t *testing.T) { sysAuthZ := integration.WithSystemAuthorization(ctx) testStartTimestamp := time.Now() - for _, relTestCase := range integration.RelationalTablesEnableMatrix(t, ctx, sysAuthZ) { - t.Run(relTestCase.Name, func(tt *testing.T) { - inst := relTestCase.Inst - client := inst.Client.OrgV2beta - instOwner := relTestCase.InstOwner - tt.Cleanup(func() { - _, err := inst.Client.InstanceV2.DeleteInstance(sysAuthZ, &instance.DeleteInstanceRequest{InstanceId: inst.ID()}) - assert.NoError(tt, err) - }) + inst := integration.NewInstance(sysAuthZ) + client := inst.Client.OrgV2beta + instOwner := inst.WithAuthorizationToken(ctx, integration.UserTypeIAMOwner) - orgs := make([]struct { - organisation *v2beta_org.CreateOrganizationResponse - name string - domain string - }, 3) + t.Cleanup(func() { + _, err := inst.Client.InstanceV2.DeleteInstance(integration.WithSystemAuthorization(context.Background()), &instance.DeleteInstanceRequest{InstanceId: inst.ID()}) + assert.NoError(t, err) + }) - for i := range orgs { - orgs[i].organisation, orgs[i].name, orgs[i].domain = createOrg(instOwner, tt, client) - } + orgs := make([]struct { + organisation *v2beta_org.CreateOrganizationResponse + name string + domain string + }, 3) - // deactivate org[1] - _, err := client.DeactivateOrganization(instOwner, &v2beta_org.DeactivateOrganizationRequest{ - Id: orgs[1].organisation.Id, - }) - require.NoError(t, err) + for i := range orgs { + orgs[i].organisation, orgs[i].name, orgs[i].domain = createOrg(instOwner, t, client) + } - // TODO: create test for when permission model is implemented in relational tables + // deactivate org[1] + _, err := client.DeactivateOrganization(instOwner, &v2beta_org.DeactivateOrganizationRequest{ + Id: orgs[1].organisation.Id, + }) + require.NoError(t, err) - happyTestCases := []struct { - name string - query []*v2beta_org.OrganizationSearchFilter - want *v2beta_org.ListOrganizationsResponse - }{ + happyTestCases := []struct { + name string + query []*v2beta_org.OrganizationSearchFilter + want *v2beta_org.ListOrganizationsResponse + }{ + { + name: "list organizations happy path, no filter", + want: &v2beta_org.ListOrganizationsResponse{ + Pagination: &filter.PaginationResponse{ + TotalResult: 4, + }, + Organizations: []*v2beta_org.Organization{ + {Id: inst.DefaultOrg.Id, Name: inst.DefaultOrg.Name}, + {Id: orgs[0].organisation.Id, Name: orgs[0].name}, + {Id: orgs[1].organisation.Id, Name: orgs[1].name}, + {Id: orgs[2].organisation.Id, Name: orgs[2].name}, + }, + }, + }, + { + name: "list organizations by id happy path", + query: []*v2beta_org.OrganizationSearchFilter{ { - name: "list organizations happy path, no filter", - want: &v2beta_org.ListOrganizationsResponse{ - Pagination: &filter.PaginationResponse{ - TotalResult: 4, - }, - Organizations: []*v2beta_org.Organization{ - {Id: inst.DefaultOrg.Id, Name: inst.DefaultOrg.Name}, - {Id: orgs[0].organisation.Id, Name: orgs[0].name}, - {Id: orgs[1].organisation.Id, Name: orgs[1].name}, - {Id: orgs[2].organisation.Id, Name: orgs[2].name}, + Filter: &v2beta_org.OrganizationSearchFilter_IdFilter{ + IdFilter: &v2beta_org.OrgIDFilter{ + Id: orgs[1].organisation.Id, }, }, }, + }, + want: &v2beta_org.ListOrganizationsResponse{ + Pagination: &filter.PaginationResponse{ + TotalResult: 1, + }, + Organizations: []*v2beta_org.Organization{ + {Id: orgs[1].organisation.Id, Name: orgs[1].name}, + }, + }, + }, + { + name: "list organizations by state active", + query: []*v2beta_org.OrganizationSearchFilter{ { - name: "list organizations by id happy path", - query: []*v2beta_org.OrganizationSearchFilter{ - { - Filter: &v2beta_org.OrganizationSearchFilter_IdFilter{ - IdFilter: &v2beta_org.OrgIDFilter{ - Id: orgs[1].organisation.Id, - }, - }, - }, - }, - want: &v2beta_org.ListOrganizationsResponse{ - Pagination: &filter.PaginationResponse{ - TotalResult: 1, - }, - Organizations: []*v2beta_org.Organization{ - {Id: orgs[1].organisation.Id, Name: orgs[1].name}, + Filter: &v2beta_org.OrganizationSearchFilter_StateFilter{ + StateFilter: &v2beta_org.OrgStateFilter{ + State: v2beta_org.OrgState_ORG_STATE_ACTIVE, }, }, }, + }, + want: &v2beta_org.ListOrganizationsResponse{ + Pagination: &filter.PaginationResponse{ + TotalResult: 3, + }, + Organizations: []*v2beta_org.Organization{ + {Id: inst.DefaultOrg.Id, Name: inst.DefaultOrg.Name}, + {Id: orgs[0].organisation.Id, Name: orgs[0].name}, + {Id: orgs[2].organisation.Id, Name: orgs[2].name}, + }, + }, + }, + { + name: "list organizations by state inactive", + query: []*v2beta_org.OrganizationSearchFilter{ { - name: "list organizations by state active", - query: []*v2beta_org.OrganizationSearchFilter{ - { - Filter: &v2beta_org.OrganizationSearchFilter_StateFilter{ - StateFilter: &v2beta_org.OrgStateFilter{ - State: v2beta_org.OrgState_ORG_STATE_ACTIVE, - }, - }, - }, - }, - want: &v2beta_org.ListOrganizationsResponse{ - Pagination: &filter.PaginationResponse{ - TotalResult: 3, - }, - Organizations: []*v2beta_org.Organization{ - {Id: inst.DefaultOrg.Id, Name: inst.DefaultOrg.Name}, - {Id: orgs[0].organisation.Id, Name: orgs[0].name}, - {Id: orgs[2].organisation.Id, Name: orgs[2].name}, + Filter: &v2beta_org.OrganizationSearchFilter_StateFilter{ + StateFilter: &v2beta_org.OrgStateFilter{ + State: v2beta_org.OrgState_ORG_STATE_INACTIVE, }, }, }, + }, + want: &v2beta_org.ListOrganizationsResponse{ + Pagination: &filter.PaginationResponse{ + TotalResult: 1, + }, + Organizations: []*v2beta_org.Organization{ + {Id: orgs[1].organisation.Id, Name: orgs[1].name}, + }, + }, + }, + { + name: "list organizations by id bad id", + query: []*v2beta_org.OrganizationSearchFilter{ { - name: "list organizations by state inactive", - query: []*v2beta_org.OrganizationSearchFilter{ - { - Filter: &v2beta_org.OrganizationSearchFilter_StateFilter{ - StateFilter: &v2beta_org.OrgStateFilter{ - State: v2beta_org.OrgState_ORG_STATE_INACTIVE, - }, - }, - }, - }, - want: &v2beta_org.ListOrganizationsResponse{ - Pagination: &filter.PaginationResponse{ - TotalResult: 1, - }, - Organizations: []*v2beta_org.Organization{ - {Id: orgs[1].organisation.Id, Name: orgs[1].name}, + Filter: &v2beta_org.OrganizationSearchFilter_IdFilter{ + IdFilter: &v2beta_org.OrgIDFilter{ + Id: "bad id", }, }, }, - { - name: "list organizations by id bad id", - query: []*v2beta_org.OrganizationSearchFilter{ - { - Filter: &v2beta_org.OrganizationSearchFilter_IdFilter{ - IdFilter: &v2beta_org.OrgIDFilter{ - Id: "bad id", - }, - }, - }, - }, - want: &v2beta_org.ListOrganizationsResponse{ - Pagination: &filter.PaginationResponse{ - TotalResult: 0, - }, - Organizations: nil, - }, + }, + want: &v2beta_org.ListOrganizationsResponse{ + Pagination: &filter.PaginationResponse{ + TotalResult: 0, }, + Organizations: nil, + }, + }, + { + name: "list organizations specify org name equals", + query: []*v2beta_org.OrganizationSearchFilter{ { - name: "list organizations specify org name equals", - query: []*v2beta_org.OrganizationSearchFilter{ - { - Filter: &v2beta_org.OrganizationSearchFilter_NameFilter{ - NameFilter: &v2beta_org.OrgNameFilter{ - Name: orgs[1].name, - Method: v2beta_object.TextQueryMethod_TEXT_QUERY_METHOD_EQUALS, - }, - }, - }, - }, - want: &v2beta_org.ListOrganizationsResponse{ - Pagination: &filter.PaginationResponse{ - TotalResult: 1, - }, - Organizations: []*v2beta_org.Organization{ - {Id: orgs[1].organisation.Id, Name: orgs[1].name}, + Filter: &v2beta_org.OrganizationSearchFilter_NameFilter{ + NameFilter: &v2beta_org.OrgNameFilter{ + Name: orgs[1].name, + Method: v2beta_object.TextQueryMethod_TEXT_QUERY_METHOD_EQUALS, }, }, }, + }, + want: &v2beta_org.ListOrganizationsResponse{ + Pagination: &filter.PaginationResponse{ + TotalResult: 1, + }, + Organizations: []*v2beta_org.Organization{ + {Id: orgs[1].organisation.Id, Name: orgs[1].name}, + }, + }, + }, + { + name: "list organizations specify org name contains", + query: []*v2beta_org.OrganizationSearchFilter{ { - name: "list organizations specify org name contains", - query: []*v2beta_org.OrganizationSearchFilter{ - { - Filter: &v2beta_org.OrganizationSearchFilter_NameFilter{ - NameFilter: &v2beta_org.OrgNameFilter{ - Name: func() string { - return orgs[1].name[1 : len(orgs[1].name)-2] - }(), - Method: v2beta_object.TextQueryMethod_TEXT_QUERY_METHOD_CONTAINS, - }, - }, - }, - }, - want: &v2beta_org.ListOrganizationsResponse{ - Pagination: &filter.PaginationResponse{ - TotalResult: 1, - }, - Organizations: []*v2beta_org.Organization{ - {Id: orgs[1].organisation.Id, Name: orgs[1].name}, + Filter: &v2beta_org.OrganizationSearchFilter_NameFilter{ + NameFilter: &v2beta_org.OrgNameFilter{ + Name: func() string { + return orgs[1].name[1 : len(orgs[1].name)-2] + }(), + Method: v2beta_object.TextQueryMethod_TEXT_QUERY_METHOD_CONTAINS, }, }, }, + }, + want: &v2beta_org.ListOrganizationsResponse{ + Pagination: &filter.PaginationResponse{ + TotalResult: 1, + }, + Organizations: []*v2beta_org.Organization{ + {Id: orgs[1].organisation.Id, Name: orgs[1].name}, + }, + }, + }, + { + name: "list organizations specify org name contains IGNORE CASE", + query: []*v2beta_org.OrganizationSearchFilter{ { - name: "list organizations specify org name contains IGNORE CASE", - query: []*v2beta_org.OrganizationSearchFilter{ - { - Filter: &v2beta_org.OrganizationSearchFilter_NameFilter{ - NameFilter: &v2beta_org.OrgNameFilter{ - Name: func() string { - return strings.ToUpper(orgs[1].name[1 : len(orgs[1].name)-2]) - }(), - Method: v2beta_object.TextQueryMethod_TEXT_QUERY_METHOD_CONTAINS_IGNORE_CASE, - }, - }, - }, - }, - want: &v2beta_org.ListOrganizationsResponse{ - Pagination: &filter.PaginationResponse{ - TotalResult: 1, - }, - Organizations: []*v2beta_org.Organization{ - {Id: orgs[1].organisation.Id, Name: orgs[1].name}, + Filter: &v2beta_org.OrganizationSearchFilter_NameFilter{ + NameFilter: &v2beta_org.OrgNameFilter{ + Name: func() string { + return strings.ToUpper(orgs[1].name[1 : len(orgs[1].name)-2]) + }(), + Method: v2beta_object.TextQueryMethod_TEXT_QUERY_METHOD_CONTAINS_IGNORE_CASE, }, }, }, + }, + want: &v2beta_org.ListOrganizationsResponse{ + Pagination: &filter.PaginationResponse{ + TotalResult: 1, + }, + Organizations: []*v2beta_org.Organization{ + {Id: orgs[1].organisation.Id, Name: orgs[1].name}, + }, + }, + }, + { + name: "list organizations specify domain name equals", + query: []*v2beta_org.OrganizationSearchFilter{ { - name: "list organizations specify domain name equals", - query: []*v2beta_org.OrganizationSearchFilter{ - { - Filter: &v2beta_org.OrganizationSearchFilter_DomainFilter{ - DomainFilter: &v2beta_org.OrgDomainFilter{ - Domain: orgs[1].domain, - Method: v2beta_object.TextQueryMethod_TEXT_QUERY_METHOD_EQUALS, - }, - }, - }, - }, - want: &v2beta_org.ListOrganizationsResponse{ - Pagination: &filter.PaginationResponse{ - TotalResult: 1, - }, - Organizations: []*v2beta_org.Organization{ - {Id: orgs[1].organisation.Id, Name: orgs[1].name}, + Filter: &v2beta_org.OrganizationSearchFilter_DomainFilter{ + DomainFilter: &v2beta_org.OrgDomainFilter{ + Domain: orgs[1].domain, + Method: v2beta_object.TextQueryMethod_TEXT_QUERY_METHOD_EQUALS, }, }, }, + }, + want: &v2beta_org.ListOrganizationsResponse{ + Pagination: &filter.PaginationResponse{ + TotalResult: 1, + }, + Organizations: []*v2beta_org.Organization{ + {Id: orgs[1].organisation.Id, Name: orgs[1].name}, + }, + }, + }, + { + name: "list organizations specify domain name contains", + query: []*v2beta_org.OrganizationSearchFilter{ { - name: "list organizations specify domain name contains", - query: []*v2beta_org.OrganizationSearchFilter{ - { - Filter: &v2beta_org.OrganizationSearchFilter_DomainFilter{ - DomainFilter: &v2beta_org.OrgDomainFilter{ - Domain: orgs[1].domain[1 : len(orgs[1].domain)-2], - Method: v2beta_object.TextQueryMethod_TEXT_QUERY_METHOD_CONTAINS, - }, - }, - }, - }, - want: &v2beta_org.ListOrganizationsResponse{ - Pagination: &filter.PaginationResponse{ - TotalResult: 1, - }, - Organizations: []*v2beta_org.Organization{ - {Id: orgs[1].organisation.Id, Name: orgs[1].name}, + Filter: &v2beta_org.OrganizationSearchFilter_DomainFilter{ + DomainFilter: &v2beta_org.OrgDomainFilter{ + Domain: orgs[1].domain[1 : len(orgs[1].domain)-2], + Method: v2beta_object.TextQueryMethod_TEXT_QUERY_METHOD_CONTAINS, }, }, }, + }, + want: &v2beta_org.ListOrganizationsResponse{ + Pagination: &filter.PaginationResponse{ + TotalResult: 1, + }, + Organizations: []*v2beta_org.Organization{ + {Id: orgs[1].organisation.Id, Name: orgs[1].name}, + }, + }, + }, + { + name: "list organizations specify org name contains IGNORE CASE", + query: []*v2beta_org.OrganizationSearchFilter{ { - name: "list organizations specify org name contains IGNORE CASE", - query: []*v2beta_org.OrganizationSearchFilter{ - { - Filter: &v2beta_org.OrganizationSearchFilter_DomainFilter{ - DomainFilter: &v2beta_org.OrgDomainFilter{ - Domain: strings.ToUpper(orgs[1].domain[1 : len(orgs[1].domain)-2]), - Method: v2beta_object.TextQueryMethod_TEXT_QUERY_METHOD_CONTAINS_IGNORE_CASE, - }, - }, - }, - }, - want: &v2beta_org.ListOrganizationsResponse{ - Pagination: &filter.PaginationResponse{ - TotalResult: 1, - }, - Organizations: []*v2beta_org.Organization{ - {Id: orgs[1].organisation.Id, Name: orgs[1].name}, + Filter: &v2beta_org.OrganizationSearchFilter_DomainFilter{ + DomainFilter: &v2beta_org.OrgDomainFilter{ + Domain: strings.ToUpper(orgs[1].domain[1 : len(orgs[1].domain)-2]), + Method: v2beta_object.TextQueryMethod_TEXT_QUERY_METHOD_CONTAINS_IGNORE_CASE, }, }, }, - } + }, + want: &v2beta_org.ListOrganizationsResponse{ + Pagination: &filter.PaginationResponse{ + TotalResult: 1, + }, + Organizations: []*v2beta_org.Organization{ + {Id: orgs[1].organisation.Id, Name: orgs[1].name}, + }, + }, + }, + } - for _, happyTestCase := range happyTestCases { - tt.Run(happyTestCase.name, func(ttt *testing.T) { - retryDuration, tick := integration.WaitForAndTickWithMaxDuration(CTX, 20*time.Second) - require.EventuallyWithT(ttt, func(tttt *assert.CollectT) { - got, err := client.ListOrganizations(instOwner, &v2beta_org.ListOrganizationsRequest{ - Filter: happyTestCase.query, - Pagination: &filter.PaginationRequest{Asc: true}, - SortingColumn: v2beta_org.OrgFieldName_ORG_FIELD_NAME_CREATION_DATE, - }) - require.NoError(tttt, err) - - require.Equal(tttt, happyTestCase.want.GetPagination(), got.GetPagination()) - - require.Len(tttt, got.Organizations, len(happyTestCase.want.Organizations)) - - for i, got := range got.Organizations { - // created/chagned date - gotCD := got.GetCreationDate().AsTime() - now := time.Now() - assert.WithinRange(tttt, gotCD, testStartTimestamp, now.Add(time.Minute)) - gotCD = got.GetChangedDate().AsTime() - assert.WithinRange(tttt, gotCD, testStartTimestamp, now.Add(time.Minute)) - - assert.Equal(tttt, happyTestCase.want.Organizations[i].Id, got.Id) - assert.Equal(tttt, happyTestCase.want.Organizations[i].Name, got.Name) - } - }, retryDuration, tick, "timeout waiting for expected organizations being created") + for _, happyTestCase := range happyTestCases { + t.Run(happyTestCase.name, func(ttt *testing.T) { + retryDuration, tick := integration.WaitForAndTickWithMaxDuration(CTX, 20*time.Second) + require.EventuallyWithT(ttt, func(tttt *assert.CollectT) { + got, err := client.ListOrganizations(instOwner, &v2beta_org.ListOrganizationsRequest{ + Filter: happyTestCase.query, + Pagination: &filter.PaginationRequest{Asc: true}, + SortingColumn: v2beta_org.OrgFieldName_ORG_FIELD_NAME_CREATION_DATE, }) - } + require.NoError(tttt, err) + + require.Equal(tttt, happyTestCase.want.GetPagination(), got.GetPagination()) + + require.Len(tttt, got.Organizations, len(happyTestCase.want.Organizations)) + + for i, got := range got.Organizations { + // created/chagned date + gotCD := got.GetCreationDate().AsTime() + now := time.Now() + assert.WithinRange(tttt, gotCD, testStartTimestamp, now.Add(time.Minute)) + gotCD = got.GetChangedDate().AsTime() + assert.WithinRange(tttt, gotCD, testStartTimestamp, now.Add(time.Minute)) + + assert.Equal(tttt, happyTestCase.want.Organizations[i].Id, got.Id) + assert.Equal(tttt, happyTestCase.want.Organizations[i].Name, got.Name) + } + }, retryDuration, tick, "timeout waiting for expected organizations being created") }) } } @@ -701,76 +689,69 @@ func TestServer_DeleteOrganization(t *testing.T) { sysAuthZ := integration.WithSystemAuthorization(ctx) - for _, relTableTestCase := range integration.RelationalTablesEnableMatrix(t, ctx, sysAuthZ) { - t.Run(relTableTestCase.Name, func(tt *testing.T) { - inst := relTableTestCase.Inst - client := inst.Client.OrgV2beta - instOwner := relTableTestCase.InstOwner + inst := integration.NewInstance(sysAuthZ) + client := inst.Client.OrgV2beta + instOwner := inst.WithAuthorizationToken(ctx, integration.UserTypeIAMOwner) - tt.Cleanup(func() { - _, err := inst.Client.InstanceV2.DeleteInstance(sysAuthZ, &instance.DeleteInstanceRequest{InstanceId: inst.ID()}) - assert.NoError(tt, err) - }) + t.Cleanup(func() { + _, err := inst.Client.InstanceV2.DeleteInstance(integration.WithSystemAuthorization(context.Background()), &instance.DeleteInstanceRequest{InstanceId: inst.ID()}) + assert.NoError(t, err) + }) - tt.Run("happy path", func(ttt *testing.T) { - // given - organisation, _, _ := createOrg(instOwner, ttt, client) + t.Run("happy path", func(ttt *testing.T) { + // given + organisation, _, _ := createOrg(instOwner, ttt, client) + + // when + got, err := client.DeleteOrganization(instOwner, &v2beta_org.DeleteOrganizationRequest{Id: organisation.Id}) + + // then + assert.NoError(ttt, err) + deletionTime := got.GetDeletionDate().AsTime() + now := time.Now() + assert.WithinRange(t, deletionTime, now.Add(-time.Minute), now.Add(time.Minute)) + }) + + t.Run("unhappy: no permission", func(ttt *testing.T) { + // given + organisation, _, _ := createOrg(instOwner, ttt, client) + usersWithoutPermissions := []integration.UserType{ + integration.UserTypeOrgOwner, + } + + for _, userType := range usersWithoutPermissions { + ttt.Run(userType.String(), func(tttt *testing.T) { + u := inst.WithAuthorizationToken(ctx, userType) // when - got, err := client.DeleteOrganization(instOwner, &v2beta_org.DeleteOrganizationRequest{Id: organisation.Id}) + _, err := client.DeleteOrganization(u, &v2beta_org.DeleteOrganizationRequest{Id: organisation.Id}) // then - assert.NoError(ttt, err) - deletionTime := got.GetDeletionDate().AsTime() - now := time.Now() - assert.WithinRange(t, deletionTime, now.Add(-time.Minute), now.Add(time.Minute)) + assert.ErrorContains(tttt, err, "membership not found") }) + } + }) - tt.Run("unhappy: no permission", func(ttt *testing.T) { - // given - organisation, _, _ := createOrg(instOwner, ttt, client) - usersWithoutPermissions := []integration.UserType{ - integration.UserTypeOrgOwner, - } + t.Run("happy: already deleted", func(ttt *testing.T) { + // given + organisation, _, _ := createOrg(instOwner, ttt, client) + _, err := client.DeleteOrganization(instOwner, &v2beta_org.DeleteOrganizationRequest{Id: organisation.Id}) + require.NoError(ttt, err) - for _, userType := range usersWithoutPermissions { - ttt.Run(userType.String(), func(tttt *testing.T) { - u := inst.WithAuthorizationToken(ctx, userType) + // when + _, err = client.DeleteOrganization(instOwner, &v2beta_org.DeleteOrganizationRequest{Id: organisation.Id}) - // when - _, err := client.DeleteOrganization(u, &v2beta_org.DeleteOrganizationRequest{Id: organisation.Id}) + // then + require.NoError(ttt, err) + }) - // then - if relTableTestCase.Enabled { - tttt.Skip("TODO: remove when permission model is implemented in relational tables") - } - assert.ErrorContains(tttt, err, "membership not found") - }) - } - }) + t.Run("happy: non existing", func(ttt *testing.T) { + // when + _, err := client.DeleteOrganization(instOwner, &v2beta_org.DeleteOrganizationRequest{Id: "non existing org id"}) - tt.Run("happy: already deleted", func(ttt *testing.T) { - // given - organisation, _, _ := createOrg(instOwner, ttt, client) - _, err := client.DeleteOrganization(instOwner, &v2beta_org.DeleteOrganizationRequest{Id: organisation.Id}) - require.NoError(ttt, err) - - // when - _, err = client.DeleteOrganization(instOwner, &v2beta_org.DeleteOrganizationRequest{Id: organisation.Id}) - - // then - require.NoError(ttt, err) - }) - - tt.Run("happy: non existing", func(ttt *testing.T) { - // when - _, err := client.DeleteOrganization(instOwner, &v2beta_org.DeleteOrganizationRequest{Id: "non existing org id"}) - - // then - require.NoError(ttt, err) - }) - }) - } + // then + require.NoError(ttt, err) + }) } func TestServer_DeactivateReactivateNonExistentOrganization(t *testing.T) { @@ -796,77 +777,70 @@ func TestServer_ActivateOrganization(t *testing.T) { sysAuthZ := integration.WithSystemAuthorization(ctx) - for _, testCase := range integration.RelationalTablesEnableMatrix(t, ctx, sysAuthZ) { - t.Run(testCase.Name, func(tt *testing.T) { - inst := testCase.Inst - client := inst.Client.OrgV2beta - instOwner := testCase.InstOwner + inst := integration.NewInstance(sysAuthZ) + client := inst.Client.OrgV2beta + instOwner := inst.WithAuthorizationToken(ctx, integration.UserTypeIAMOwner) - tt.Cleanup(func() { - _, err := inst.Client.InstanceV2.DeleteInstance(sysAuthZ, &instance.DeleteInstanceRequest{InstanceId: inst.ID()}) - assert.NoError(tt, err) - }) + t.Cleanup(func() { + _, err := inst.Client.InstanceV2.DeleteInstance(integration.WithSystemAuthorization(context.Background()), &instance.DeleteInstanceRequest{InstanceId: inst.ID()}) + assert.NoError(t, err) + }) - tt.Run("Happy path", func(ttt *testing.T) { - // given - organisation, _, _ := createOrg(instOwner, ttt, client) + t.Run("Happy path", func(ttt *testing.T) { + // given + organisation, _, _ := createOrg(instOwner, ttt, client) - _, err := client.DeactivateOrganization(instOwner, &v2beta_org.DeactivateOrganizationRequest{Id: organisation.Id}) - require.NoError(ttt, err) + _, err := client.DeactivateOrganization(instOwner, &v2beta_org.DeactivateOrganizationRequest{Id: organisation.Id}) + require.NoError(ttt, err) + + // when + _, err = client.ActivateOrganization(instOwner, &v2beta_org.ActivateOrganizationRequest{Id: organisation.Id}) + + // then + assert.NoError(ttt, err) + }) + + t.Run("Unhappy: no permission", func(ttt *testing.T) { + // given + organisation, _, _ := createOrg(instOwner, ttt, client) + usersWithoutPermissions := []integration.UserType{ + integration.UserTypeOrgOwner, + } + + _, err := client.DeactivateOrganization(instOwner, &v2beta_org.DeactivateOrganizationRequest{Id: organisation.Id}) + assert.NoError(ttt, err) + + for _, userType := range usersWithoutPermissions { + ttt.Run(userType.String(), func(tttt *testing.T) { + u := inst.WithAuthorizationToken(ctx, userType) // when - _, err = client.ActivateOrganization(instOwner, &v2beta_org.ActivateOrganizationRequest{Id: organisation.Id}) + _, err = client.ActivateOrganization(u, &v2beta_org.ActivateOrganizationRequest{Id: organisation.Id}) // then - assert.NoError(ttt, err) + assert.ErrorContains(tttt, err, "membership not found") }) + } + }) - tt.Run("Unhappy: no permission", func(ttt *testing.T) { - // given - organisation, _, _ := createOrg(instOwner, ttt, client) - usersWithoutPermissions := []integration.UserType{ - integration.UserTypeOrgOwner, - } + t.Run("Unhappy: unknown org", func(ttt *testing.T) { + // when + _, err := client.ActivateOrganization(instOwner, &v2beta_org.ActivateOrganizationRequest{Id: "does not exist"}) - _, err := client.DeactivateOrganization(instOwner, &v2beta_org.DeactivateOrganizationRequest{Id: organisation.Id}) - assert.NoError(ttt, err) + // then + assert.ErrorContains(ttt, err, "Organisation not found") + }) - for _, userType := range usersWithoutPermissions { - ttt.Run(userType.String(), func(tttt *testing.T) { - u := inst.WithAuthorizationToken(ctx, userType) + t.Run("Unhappy: already activated", func(ttt *testing.T) { + // given + organisation, _, _ := createOrg(instOwner, ttt, client) - // when - _, err = client.ActivateOrganization(u, &v2beta_org.ActivateOrganizationRequest{Id: organisation.Id}) + // when + _, err := client.ActivateOrganization(instOwner, &v2beta_org.ActivateOrganizationRequest{Id: organisation.Id}) - // then - if testCase.Enabled { - tttt.Skip("TODO: remove when permission model is implemented in relational tables") - } - assert.ErrorContains(tttt, err, "membership not found") - }) - } - }) - - tt.Run("Unhappy: unknown org", func(ttt *testing.T) { - // when - _, err := client.ActivateOrganization(instOwner, &v2beta_org.ActivateOrganizationRequest{Id: "does not exist"}) - - // then - assert.ErrorContains(ttt, err, "Organisation not found") - }) - - tt.Run("Unhappy: already activated", func(ttt *testing.T) { - // given - organisation, _, _ := createOrg(instOwner, ttt, client) - - // when - _, err := client.ActivateOrganization(instOwner, &v2beta_org.ActivateOrganizationRequest{Id: organisation.Id}) - - // then - assert.ErrorContains(ttt, err, "Organisation is already active") - }) - }) - } + // then + assert.ErrorContains(ttt, err, "Organisation is already active") + }) } func TestServer_DeactivateOrganization(t *testing.T) { @@ -875,73 +849,67 @@ func TestServer_DeactivateOrganization(t *testing.T) { defer cancel() sysAuthZ := integration.WithSystemAuthorization(ctx) - for _, testCase := range integration.RelationalTablesEnableMatrix(t, ctx, sysAuthZ) { - t.Run(testCase.Name, func(tt *testing.T) { - inst := testCase.Inst - client := inst.Client.OrgV2beta - instOwner := testCase.InstOwner - tt.Cleanup(func() { - _, err := inst.Client.InstanceV2.DeleteInstance(sysAuthZ, &instance.DeleteInstanceRequest{InstanceId: inst.ID()}) - require.NoError(tt, err) - }) + inst := integration.NewInstance(sysAuthZ) + client := inst.Client.OrgV2beta + instOwner := inst.WithAuthorizationToken(ctx, integration.UserTypeIAMOwner) - tt.Run("Happy path", func(ttt *testing.T) { - // given - organisation, _, _ := createOrg(instOwner, ttt, client) + t.Cleanup(func() { + _, err := inst.Client.InstanceV2.DeleteInstance(integration.WithSystemAuthorization(context.Background()), &instance.DeleteInstanceRequest{InstanceId: inst.ID()}) + require.NoError(t, err) + }) + + t.Run("Happy path", func(ttt *testing.T) { + // given + organisation, _, _ := createOrg(instOwner, ttt, client) + + // when + _, err := client.DeactivateOrganization(instOwner, &v2beta_org.DeactivateOrganizationRequest{Id: organisation.Id}) + + // then + assert.NoError(ttt, err) + }) + + t.Run("Unhappy: no permission", func(ttt *testing.T) { + // given + organisation, _, _ := createOrg(instOwner, ttt, client) + usersWithoutPermissions := []integration.UserType{ + integration.UserTypeOrgOwner, + } + + for _, userType := range usersWithoutPermissions { + ttt.Run(userType.String(), func(tttt *testing.T) { + u := inst.WithAuthorizationToken(ctx, userType) // when - _, err := client.DeactivateOrganization(instOwner, &v2beta_org.DeactivateOrganizationRequest{Id: organisation.Id}) + _, err := client.DeactivateOrganization(u, &v2beta_org.DeactivateOrganizationRequest{Id: organisation.Id}) // then - assert.NoError(ttt, err) + assert.ErrorContains(tttt, err, "membership not found") }) + } + }) - tt.Run("Unhappy: no permission", func(ttt *testing.T) { - // given - organisation, _, _ := createOrg(instOwner, ttt, client) - usersWithoutPermissions := []integration.UserType{ - integration.UserTypeOrgOwner, - } + t.Run("Unhappy: unknown org", func(ttt *testing.T) { + // when + _, err := client.DeactivateOrganization(instOwner, &v2beta_org.DeactivateOrganizationRequest{Id: "does not exist"}) - for _, userType := range usersWithoutPermissions { - ttt.Run(userType.String(), func(tttt *testing.T) { - u := inst.WithAuthorizationToken(ctx, userType) + // then + assert.ErrorContains(ttt, err, "Organisation not found") + }) - // when - _, err := client.DeactivateOrganization(u, &v2beta_org.DeactivateOrganizationRequest{Id: organisation.Id}) + t.Run("Unhappy: already deactivated", func(ttt *testing.T) { + // given + organisation, _, _ := createOrg(instOwner, ttt, client) + _, err := client.DeactivateOrganization(instOwner, &v2beta_org.DeactivateOrganizationRequest{Id: organisation.Id}) + require.NoError(ttt, err) - // then - if testCase.Enabled { - tttt.Skip("TODO: remove when permission model is implemented in relational tables") - } - assert.ErrorContains(tttt, err, "membership not found") - }) - } - }) + // when + _, err = client.DeactivateOrganization(instOwner, &v2beta_org.DeactivateOrganizationRequest{Id: organisation.Id}) - tt.Run("Unhappy: unknown org", func(ttt *testing.T) { - // when - _, err := client.DeactivateOrganization(instOwner, &v2beta_org.DeactivateOrganizationRequest{Id: "does not exist"}) - - // then - assert.ErrorContains(ttt, err, "Organisation not found") - }) - - tt.Run("Unhappy: already deactivated", func(ttt *testing.T) { - // given - organisation, _, _ := createOrg(instOwner, ttt, client) - _, err := client.DeactivateOrganization(instOwner, &v2beta_org.DeactivateOrganizationRequest{Id: organisation.Id}) - require.NoError(ttt, err) - - // when - _, err = client.DeactivateOrganization(instOwner, &v2beta_org.DeactivateOrganizationRequest{Id: organisation.Id}) - - // then - assert.ErrorContains(ttt, err, "Organisation is already deactivated") - }) - }) - } + // then + assert.ErrorContains(ttt, err, "Organisation is already deactivated") + }) } func TestServer_AddOrganizationDomain(t *testing.T) { diff --git a/internal/api/grpc/session/v2/integration_test/query_test.go b/internal/api/grpc/session/v2/integration_test/query_test.go index 914eba0d0..74da5e0df 100644 --- a/internal/api/grpc/session/v2/integration_test/query_test.go +++ b/internal/api/grpc/session/v2/integration_test/query_test.go @@ -15,8 +15,6 @@ import ( "google.golang.org/protobuf/types/known/timestamppb" "github.com/zitadel/zitadel/internal/integration" - featurepb "github.com/zitadel/zitadel/pkg/grpc/feature/v2" - instancepb "github.com/zitadel/zitadel/pkg/grpc/instance/v2" objpb "github.com/zitadel/zitadel/pkg/grpc/object" "github.com/zitadel/zitadel/pkg/grpc/object/v2" "github.com/zitadel/zitadel/pkg/grpc/session/v2" @@ -250,8 +248,7 @@ type sessionAttr struct { Details *object.Details } -// listSessionsEnv holds the environment context for ListSessions tests, -// allowing the same test cases to run against both the eventstore and relational backends. +// listSessionsEnv holds the environment context for ListSessions tests. type listSessionsEnv struct { ownerCtx context.Context // IAMOwner-level context; can list all sessions loginCtx context.Context // login-user context; used for session creation in deps @@ -260,13 +257,9 @@ type listSessionsEnv struct { loginUserID string testUser *user.AddHumanUserResponse newUser func(ctx context.Context) *user.AddHumanUserResponse - // isRelational controls behavior that currently differs between the eventstore - // and relational backends (e.g. sequence field, permission-check semantics). - isRelational bool } // runListSessionsTestCases defines and runs the full ListSessions test suite against env. -// This is called by both [TestServer_ListSessions] (eventstore) and [TestServer_ListSessions_Relational]. func runListSessionsTestCases(t *testing.T, env listSessionsEnv) { t.Helper() @@ -331,29 +324,17 @@ func runListSessionsTestCases(t *testing.T, env listSessionsEnv) { }, }, { - // In the eventstore path, the session exists (TotalResult=1) but the permission - // callback hides its content (Sessions=[]). In the relational path the permission - // check is not yet implemented (returns SQL true), so the session is fully visible. - // TODO(IAM-Marco): align expectations once relational permission checks are implemented. name: "list sessions, no permission", ctx: env.noPermCtx, req: &session.ListSessionsRequest{Queries: []*session.SearchQuery{}}, dep: func(t *testing.T, req *session.ListSessionsRequest) []*sessionAttr { info := newSess(t, env.loginCtx, "", "", nil, nil) req.Queries = append(req.Queries, &session.SearchQuery{Query: &session.SearchQuery_IdsQuery{IdsQuery: &session.IDsQuery{Ids: []string{info.ID}}}}) - if env.isRelational { - return []*sessionAttr{info} - } return []*sessionAttr{} }, want: &session.ListSessionsResponse{ - Details: &object.ListDetails{TotalResult: 1, Timestamp: timestamppb.Now()}, - Sessions: func() []*session.Session { - if env.isRelational { - return []*session.Session{{}} - } - return []*session.Session{} - }(), + Details: &object.ListDetails{TotalResult: 1, Timestamp: timestamppb.Now()}, + Sessions: []*session.Session{}, }, }, { @@ -670,11 +651,7 @@ func runListSessionsTestCases(t *testing.T, env listSessionsEnv) { for i := range infos { tt.want.Sessions[i].Id = infos[i].ID - if env.isRelational { - tt.want.Sessions[i].Sequence = 0 - } else { - tt.want.Sessions[i].Sequence = infos[i].Details.GetSequence() - } + tt.want.Sessions[i].Sequence = infos[i].Details.GetSequence() tt.want.Sessions[i].CreationDate = infos[i].Details.GetChangeDate() tt.want.Sessions[i].ChangeDate = infos[i].Details.GetChangeDate() @@ -765,37 +742,3 @@ func TestServer_ListSessions_with_expiration_date_filter(t *testing.T) { assert.Equal(t, session2.SessionId, listSessionsResponse2.Sessions[0].Id) } -func TestServer_ListSessions_Relational(t *testing.T) { - t.Parallel() - - ctx, cancel := context.WithTimeout(context.Background(), 15*time.Minute) - defer cancel() - - sysAuthZ := integration.WithSystemAuthorization(ctx) - - inst := integration.NewInstance(sysAuthZ) - integration.EnsureInstanceFeature(t, sysAuthZ, inst, - &featurepb.SetInstanceFeaturesRequest{EnableRelationalTables: gu.Ptr(true)}, - func(tCollect *assert.CollectT, got *featurepb.GetInstanceFeaturesResponse) { - assert.True(tCollect, got.GetEnableRelationalTables().GetEnabled()) - }, - ) - t.Cleanup(func() { - inst.Client.InstanceV2.DeleteInstance(sysAuthZ, &instancepb.DeleteInstanceRequest{InstanceId: inst.ID()}) - }) - - instOwnerCtx := inst.WithAuthorizationToken(context.Background(), integration.UserTypeIAMOwner) - loginCtx := inst.WithAuthorizationToken(context.Background(), integration.UserTypeLogin) - noPermCtx := inst.WithAuthorizationToken(context.Background(), integration.UserTypeNoPermission) - - runListSessionsTestCases(t, listSessionsEnv{ - ownerCtx: instOwnerCtx, - loginCtx: loginCtx, - noPermCtx: noPermCtx, - client: inst.Client.SessionV2, - loginUserID: inst.Users.Get(integration.UserTypeLogin).ID, - testUser: inst.CreateHumanUser(instOwnerCtx), - newUser: func(ctx context.Context) *user.AddHumanUserResponse { return inst.CreateHumanUser(ctx) }, - isRelational: true, - }) -} diff --git a/internal/api/grpc/session/v2/integration_test/session_test.go b/internal/api/grpc/session/v2/integration_test/session_test.go index 14588e0de..f32b636d7 100644 --- a/internal/api/grpc/session/v2/integration_test/session_test.go +++ b/internal/api/grpc/session/v2/integration_test/session_test.go @@ -19,10 +19,10 @@ import ( "google.golang.org/protobuf/types/known/durationpb" "google.golang.org/protobuf/types/known/timestamppb" - "github.com/zitadel/zitadel/backend/v3/domain" "github.com/zitadel/zitadel/internal/integration" "github.com/zitadel/zitadel/internal/integration/sink" "github.com/zitadel/zitadel/pkg/grpc/feature/v2" + "github.com/zitadel/zitadel/pkg/grpc/instance/v2" mgmt "github.com/zitadel/zitadel/pkg/grpc/management" "github.com/zitadel/zitadel/pkg/grpc/object/v2" "github.com/zitadel/zitadel/pkg/grpc/session/v2" @@ -901,120 +901,106 @@ func TestServer_SetSession_expired(t *testing.T) { func TestServer_DeleteSession_token(t *testing.T) { sysAuthZ := integration.WithSystemAuthorization(CTX) - relTableState := integration.RelationalTablesEnableMatrix(t, IAMOwnerCTX, sysAuthZ) - for _, stateCase := range relTableState { - t.Run(stateCase.Name, func(t *testing.T) { - loginCTX := stateCase.Inst.WithAuthorizationToken(t.Context(), integration.UserTypeLogin) - createResp, err := stateCase.Inst.Client.SessionV2.CreateSession(loginCTX, &session.CreateSessionRequest{}) - require.NoError(t, err) + inst := integration.NewInstance(sysAuthZ) + instOwner := inst.WithAuthorizationToken(IAMOwnerCTX, integration.UserTypeIAMOwner) +t.Cleanup(func() { + _, err := inst.Client.InstanceV2.DeleteInstance(integration.WithSystemAuthorization(context.Background()), &instance.DeleteInstanceRequest{InstanceId: inst.ID()}) + require.NoError(t, err) +}) - //TODO: remove after create session also uses the relational tables directly - if stateCase.Enabled { - time.Sleep(5 * time.Second) - } + loginCTX := inst.WithAuthorizationToken(t.Context(), integration.UserTypeLogin) + createResp, err := inst.Client.SessionV2.CreateSession(loginCTX, &session.CreateSessionRequest{}) + require.NoError(t, err) - _, err = stateCase.Inst.Client.SessionV2.DeleteSession(stateCase.InstOwner, &session.DeleteSessionRequest{ - SessionId: createResp.GetSessionId(), - SessionToken: gu.Ptr("invalid"), - }) - require.Error(t, err) - if stateCase.Enabled { - integration.RequireStatusError(t, err, domain.ErrSessionTokenInvalid(nil)) - } + _, err = inst.Client.SessionV2.DeleteSession(instOwner, &session.DeleteSessionRequest{ + SessionId: createResp.GetSessionId(), + SessionToken: gu.Ptr("invalid"), + }) + require.Error(t, err) - _, err = stateCase.Inst.Client.SessionV2.DeleteSession(stateCase.InstOwner, &session.DeleteSessionRequest{ - SessionId: createResp.GetSessionId(), - SessionToken: gu.Ptr(createResp.GetSessionToken()), - }) - require.NoError(t, err) - }) - } + _, err = inst.Client.SessionV2.DeleteSession(instOwner, &session.DeleteSessionRequest{ + SessionId: createResp.GetSessionId(), + SessionToken: gu.Ptr(createResp.GetSessionToken()), + }) + require.NoError(t, err) } func TestServer_DeleteSession_own_session(t *testing.T) { sysAuthZ := integration.WithSystemAuthorization(CTX) - relTableState := integration.RelationalTablesEnableMatrix(t, IAMOwnerCTX, sysAuthZ) - for _, stateCase := range relTableState { - t.Run(stateCase.Name, func(t *testing.T) { - loginCTX := stateCase.Inst.WithAuthorizationToken(t.Context(), integration.UserTypeLogin) - // create two users for the test and a session each to get tokens for authorization - user1 := stateCase.Inst.CreateUserTypeHuman(stateCase.InstOwner, integration.Email()) - stateCase.Inst.SetUserPassword(stateCase.InstOwner, user1.GetId(), integration.UserPassword, false) - _, token1, _, _ := stateCase.Inst.CreatePasswordSession(t, loginCTX, user1.GetId(), integration.UserPassword) + inst := integration.NewInstance(sysAuthZ) + instOwner := inst.WithAuthorizationToken(IAMOwnerCTX, integration.UserTypeIAMOwner) +t.Cleanup(func() { + _, err := inst.Client.InstanceV2.DeleteInstance(integration.WithSystemAuthorization(context.Background()), &instance.DeleteInstanceRequest{InstanceId: inst.ID()}) + require.NoError(t, err) +}) - user2 := stateCase.Inst.CreateUserTypeHuman(stateCase.InstOwner, integration.Email()) - stateCase.Inst.SetUserPassword(stateCase.InstOwner, user2.GetId(), integration.UserPassword, false) - _, token2, _, _ := stateCase.Inst.CreatePasswordSession(t, loginCTX, user2.GetId(), integration.UserPassword) + loginCTX := inst.WithAuthorizationToken(t.Context(), integration.UserTypeLogin) + // create two users for the test and a session each to get tokens for authorization + user1 := inst.CreateUserTypeHuman(instOwner, integration.Email()) + inst.SetUserPassword(instOwner, user1.GetId(), integration.UserPassword, false) + _, token1, _, _ := inst.CreatePasswordSession(t, loginCTX, user1.GetId(), integration.UserPassword) - // create a new session for the first user - createResp, err := stateCase.Inst.Client.SessionV2.CreateSession(loginCTX, &session.CreateSessionRequest{ - Checks: &session.Checks{ - User: &session.CheckUser{ - Search: &session.CheckUser_UserId{ - UserId: user1.GetId(), - }, - }, + user2 := inst.CreateUserTypeHuman(instOwner, integration.Email()) + inst.SetUserPassword(instOwner, user2.GetId(), integration.UserPassword, false) + _, token2, _, _ := inst.CreatePasswordSession(t, loginCTX, user2.GetId(), integration.UserPassword) + + // create a new session for the first user + createResp, err := inst.Client.SessionV2.CreateSession(loginCTX, &session.CreateSessionRequest{ + Checks: &session.Checks{ + User: &session.CheckUser{ + Search: &session.CheckUser_UserId{ + UserId: user1.GetId(), }, - }) - require.NoError(t, err) + }, + }, + }) + require.NoError(t, err) - //TODO: remove after create session also uses the relational tables directly - if stateCase.Enabled { - time.Sleep(5 * time.Second) - } + // delete the new (user1) session must not be possible with user (has no permission) + _, err = inst.Client.SessionV2.DeleteSession(integration.WithAuthorizationToken(t.Context(), token2), &session.DeleteSessionRequest{ + SessionId: createResp.GetSessionId(), + }) + require.Error(t, err) - // delete the new (user1) session must not be possible with user (has no permission) - _, err = stateCase.Inst.Client.SessionV2.DeleteSession(integration.WithAuthorizationToken(t.Context(), token2), &session.DeleteSessionRequest{ - SessionId: createResp.GetSessionId(), - }) - require.Error(t, err) - if stateCase.Enabled { - integration.RequireStatusError(t, err, domain.ErrAuthMissingPermission(nil, "", domain.SessionDeletePermission)) - } - - // delete the new (user1) session by themselves - _, err = stateCase.Inst.Client.SessionV2.DeleteSession(integration.WithAuthorizationToken(t.Context(), token1), &session.DeleteSessionRequest{ - SessionId: createResp.GetSessionId(), - }) - require.NoError(t, err) - }) - } + // delete the new (user1) session by themselves + _, err = inst.Client.SessionV2.DeleteSession(integration.WithAuthorizationToken(t.Context(), token1), &session.DeleteSessionRequest{ + SessionId: createResp.GetSessionId(), + }) + require.NoError(t, err) } func TestServer_DeleteSession_with_permission(t *testing.T) { sysAuthZ := integration.WithSystemAuthorization(CTX) - relTableState := integration.RelationalTablesEnableMatrix(t, IAMOwnerCTX, sysAuthZ) + inst := integration.NewInstance(sysAuthZ) + instOwner := inst.WithAuthorizationToken(IAMOwnerCTX, integration.UserTypeIAMOwner) +t.Cleanup(func() { + _, err := inst.Client.InstanceV2.DeleteInstance(integration.WithSystemAuthorization(context.Background()), &instance.DeleteInstanceRequest{InstanceId: inst.ID()}) + require.NoError(t, err) +}) - for _, stateCase := range relTableState { - t.Run(stateCase.Name, func(t *testing.T) { - loginCTX := stateCase.Inst.WithAuthorizationToken(t.Context(), integration.UserTypeLogin) - user1 := stateCase.Inst.CreateUserTypeHuman(stateCase.InstOwner, integration.Email()) - createResp, err := stateCase.Inst.Client.SessionV2.CreateSession(loginCTX, &session.CreateSessionRequest{ - Checks: &session.Checks{ - User: &session.CheckUser{ - Search: &session.CheckUser_UserId{ - UserId: user1.GetId(), - }, - }, + loginCTX := inst.WithAuthorizationToken(t.Context(), integration.UserTypeLogin) + user1 := inst.CreateUserTypeHuman(instOwner, integration.Email()) + createResp, err := inst.Client.SessionV2.CreateSession(loginCTX, &session.CreateSessionRequest{ + Checks: &session.Checks{ + User: &session.CheckUser{ + Search: &session.CheckUser_UserId{ + UserId: user1.GetId(), }, - }) - require.NoError(t, err) + }, + }, + }) + require.NoError(t, err) - if stateCase.Enabled { - t.Skip("TODO: remove after permission checks are implemented in the relational tables") - } - // delete the new session by ORG_OWNER - _, err = stateCase.Inst.Client.SessionV2.DeleteSession( - stateCase.Inst.WithAuthorizationToken(t.Context(), integration.UserTypeOrgOwner), - &session.DeleteSessionRequest{ - SessionId: createResp.GetSessionId(), - }, - ) - require.NoError(t, err) - }) - } + // delete the new session by ORG_OWNER + _, err = inst.Client.SessionV2.DeleteSession( + inst.WithAuthorizationToken(t.Context(), integration.UserTypeOrgOwner), + &session.DeleteSessionRequest{ + SessionId: createResp.GetSessionId(), + }, + ) + require.NoError(t, err) } func TestServer_DeleteSession_expired(t *testing.T) { @@ -1024,32 +1010,31 @@ func TestServer_DeleteSession_expired(t *testing.T) { }) sysAuthZ := integration.WithSystemAuthorization(CTX) - relTableState := integration.RelationalTablesEnableMatrix(t, IAMOwnerCTX, sysAuthZ) + inst := integration.NewInstance(sysAuthZ) +t.Cleanup(func() { + _, err := inst.Client.InstanceV2.DeleteInstance(integration.WithSystemAuthorization(context.Background()), &instance.DeleteInstanceRequest{InstanceId: inst.ID()}) + require.NoError(t, err) +}) - for _, stateCase := range relTableState { - t.Run(stateCase.Name, func(t *testing.T) { - loginCTX := stateCase.Inst.WithAuthorizationToken(t.Context(), integration.UserTypeLogin) - createResp, err := stateCase.Inst.Client.SessionV2.CreateSession(loginCTX, &session.CreateSessionRequest{ - Lifetime: durationpb.New(5 * time.Second), - }) - require.NoError(t, err) + loginCTX := inst.WithAuthorizationToken(t.Context(), integration.UserTypeLogin) + createResp, err := inst.Client.SessionV2.CreateSession(loginCTX, &session.CreateSessionRequest{ + Lifetime: durationpb.New(5 * time.Second), + }) + require.NoError(t, err) - // wait until the token expires - time.Sleep(10 * time.Second) - _, err = stateCase.Inst.Client.SessionV2.DeleteSession(stateCase.Inst.WithAuthorizationToken(t.Context(), integration.UserTypeOrgOwner), &session.DeleteSessionRequest{ - SessionId: createResp.GetSessionId(), - SessionToken: gu.Ptr(createResp.GetSessionToken()), - }) - require.NoError(t, err) + // wait until the token expires + time.Sleep(10 * time.Second) + _, err = inst.Client.SessionV2.DeleteSession(inst.WithAuthorizationToken(t.Context(), integration.UserTypeOrgOwner), &session.DeleteSessionRequest{ + SessionId: createResp.GetSessionId(), + SessionToken: gu.Ptr(createResp.GetSessionToken()), + }) + require.NoError(t, err) - // get session should return an error - sessionResp, err := stateCase.Inst.Client.SessionV2.GetSession(stateCase.Inst.WithAuthorizationToken(t.Context(), integration.UserTypeOrgOwner), - &session.GetSessionRequest{SessionId: createResp.GetSessionId()}) - require.Error(t, err) - // TODO: implement error check once the query side is relational - require.Nil(t, sessionResp) - }) - } + // get session should return an error + sessionResp, err := inst.Client.SessionV2.GetSession(inst.WithAuthorizationToken(t.Context(), integration.UserTypeOrgOwner), + &session.GetSessionRequest{SessionId: createResp.GetSessionId()}) + require.Error(t, err) + require.Nil(t, sessionResp) } func Test_ZITADEL_API_missing_authentication(t *testing.T) { diff --git a/internal/integration/feature.go b/internal/integration/feature.go index 4d8920859..07942fcdc 100644 --- a/internal/integration/feature.go +++ b/internal/integration/feature.go @@ -5,7 +5,6 @@ import ( "testing" "time" - "github.com/muhlemmer/gu" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -29,40 +28,3 @@ func EnsureInstanceFeature(t *testing.T, ctx context.Context, instance *Instance tick, "timed out waiting for ensuring instance feature") } - -type RelationalTableFeatureMatrix struct { - Name string - Inst *Instance - InstOwner context.Context - Enabled bool -} - -// TODO(IAM-Marco): Once we have gotten rid of eventstore, this can be removed -func RelationalTablesEnableMatrix(t *testing.T, ctx context.Context, sysAuthZ context.Context) []RelationalTableFeatureMatrix { - return []RelationalTableFeatureMatrix{ - func() RelationalTableFeatureMatrix { - inst := NewInstance(sysAuthZ) - instOwner := inst.WithAuthorizationToken(ctx, UserTypeIAMOwner) - return RelationalTableFeatureMatrix{ - Name: "when relational tables are disabled", - Inst: inst, - InstOwner: instOwner, - } - }(), - func() RelationalTableFeatureMatrix { - inst := NewInstance(sysAuthZ) - instOwner := inst.WithAuthorizationToken(ctx, UserTypeIAMOwner) - EnsureInstanceFeature(t, ctx, inst, &feature.SetInstanceFeaturesRequest{ - EnableRelationalTables: gu.Ptr(true), - }, func(tt *assert.CollectT, got *feature.GetInstanceFeaturesResponse) { - assert.True(tt, got.GetEnableRelationalTables().GetEnabled(), "expected relational tables to be enabled") - }) - return RelationalTableFeatureMatrix{ - Name: "when relational tables are enabled", - Inst: inst, - InstOwner: instOwner, - Enabled: true, - } - }(), - } -}