Files
zitadel/internal/api/grpc/action/v2beta/integration_test/query_test.go
Stefan Benz 5403be7c4b feat: user profile requests in resource APIs (#10151)
# Which Problems Are Solved

The commands for the resource based v2beta AuthorizationService API are
added.
Authorizations, previously knows as user grants, give a user in a
specific organization and project context roles.
The project can be owned or granted.
The given roles can be used to restrict access within the projects
applications.

The commands for the resource based v2beta InteralPermissionService API
are added.
Administrators, previously knows as memberships, give a user in a
specific organization and project context roles.
The project can be owned or granted.
The give roles give the user permissions to manage different resources
in Zitadel.

API definitions from https://github.com/zitadel/zitadel/issues/9165 are
implemented.

Contains endpoints for user metadata.

# How the Problems Are Solved

### New Methods

- CreateAuthorization
- UpdateAuthorization
- DeleteAuthorization
- ActivateAuthorization
- DeactivateAuthorization
- ListAuthorizations
- CreateAdministrator
- UpdateAdministrator
- DeleteAdministrator
- ListAdministrators
- SetUserMetadata to set metadata on a user
- DeleteUserMetadata to delete metadata on a user
- ListUserMetadata to query for metadata of a user

## Deprecated Methods

### v1.ManagementService
- GetUserGrantByID
- ListUserGrants
- AddUserGrant
- UpdateUserGrant
- DeactivateUserGrant
- ReactivateUserGrant
- RemoveUserGrant
- BulkRemoveUserGrant

### v1.AuthService
- ListMyUserGrants
- ListMyProjectPermissions

# Additional Changes

- Permission checks for metadata functionality on query and command side
- correct existence checks for resources, for example you can only be an
administrator on an existing project
- combined all member tables to singular query for the administrators
- add permission checks for command an query side functionality
- combined functions on command side where necessary for easier
maintainability

# Additional Context

Closes #9165

---------

Co-authored-by: Elio Bischof <elio@zitadel.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Livio Spring <livio.a@gmail.com>
2025-07-04 18:12:59 +02:00

785 lines
27 KiB
Go

//go:build integration
package action_test
import (
"context"
"testing"
"time"
"github.com/brianvoe/gofakeit/v6"
"github.com/muhlemmer/gu"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/types/known/durationpb"
"github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/integration"
action "github.com/zitadel/zitadel/pkg/grpc/action/v2beta"
filter "github.com/zitadel/zitadel/pkg/grpc/filter/v2beta"
)
func TestServer_GetTarget(t *testing.T) {
instance := integration.NewInstance(CTX)
isolatedIAMOwnerCTX := instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
type args struct {
ctx context.Context
dep func(context.Context, *action.GetTargetRequest, *action.GetTargetResponse) error
req *action.GetTargetRequest
}
tests := []struct {
name string
args args
want *action.GetTargetResponse
wantErr bool
}{
{
name: "missing permission",
args: args{
ctx: instance.WithAuthorization(context.Background(), integration.UserTypeOrgOwner),
req: &action.GetTargetRequest{},
},
wantErr: true,
},
{
name: "not found",
args: args{
ctx: isolatedIAMOwnerCTX,
req: &action.GetTargetRequest{Id: "notexisting"},
},
wantErr: true,
},
{
name: "get, ok",
args: args{
ctx: isolatedIAMOwnerCTX,
dep: func(ctx context.Context, request *action.GetTargetRequest, response *action.GetTargetResponse) error {
name := gofakeit.Name()
resp := instance.CreateTarget(ctx, t, name, "https://example.com", domain.TargetTypeWebhook, false)
request.Id = resp.GetId()
response.Target.Id = resp.GetId()
response.Target.Name = name
response.Target.CreationDate = resp.GetCreationDate()
response.Target.ChangeDate = resp.GetCreationDate()
response.Target.SigningKey = resp.GetSigningKey()
return nil
},
req: &action.GetTargetRequest{},
},
want: &action.GetTargetResponse{
Target: &action.Target{
Endpoint: "https://example.com",
TargetType: &action.Target_RestWebhook{
RestWebhook: &action.RESTWebhook{},
},
Timeout: durationpb.New(5 * time.Second),
},
},
},
{
name: "get, async, ok",
args: args{
ctx: isolatedIAMOwnerCTX,
dep: func(ctx context.Context, request *action.GetTargetRequest, response *action.GetTargetResponse) error {
name := gofakeit.Name()
resp := instance.CreateTarget(ctx, t, name, "https://example.com", domain.TargetTypeAsync, false)
request.Id = resp.GetId()
response.Target.Id = resp.GetId()
response.Target.Name = name
response.Target.CreationDate = resp.GetCreationDate()
response.Target.ChangeDate = resp.GetCreationDate()
response.Target.SigningKey = resp.GetSigningKey()
return nil
},
req: &action.GetTargetRequest{},
},
want: &action.GetTargetResponse{
Target: &action.Target{
Endpoint: "https://example.com",
TargetType: &action.Target_RestAsync{
RestAsync: &action.RESTAsync{},
},
Timeout: durationpb.New(5 * time.Second),
},
},
},
{
name: "get, webhook interruptOnError, ok",
args: args{
ctx: isolatedIAMOwnerCTX,
dep: func(ctx context.Context, request *action.GetTargetRequest, response *action.GetTargetResponse) error {
name := gofakeit.Name()
resp := instance.CreateTarget(ctx, t, name, "https://example.com", domain.TargetTypeWebhook, true)
request.Id = resp.GetId()
response.Target.Id = resp.GetId()
response.Target.Name = name
response.Target.CreationDate = resp.GetCreationDate()
response.Target.ChangeDate = resp.GetCreationDate()
response.Target.SigningKey = resp.GetSigningKey()
return nil
},
req: &action.GetTargetRequest{},
},
want: &action.GetTargetResponse{
Target: &action.Target{
Endpoint: "https://example.com",
TargetType: &action.Target_RestWebhook{
RestWebhook: &action.RESTWebhook{
InterruptOnError: true,
},
},
Timeout: durationpb.New(5 * time.Second),
},
},
},
{
name: "get, call, ok",
args: args{
ctx: isolatedIAMOwnerCTX,
dep: func(ctx context.Context, request *action.GetTargetRequest, response *action.GetTargetResponse) error {
name := gofakeit.Name()
resp := instance.CreateTarget(ctx, t, name, "https://example.com", domain.TargetTypeCall, false)
request.Id = resp.GetId()
response.Target.Id = resp.GetId()
response.Target.Name = name
response.Target.CreationDate = resp.GetCreationDate()
response.Target.ChangeDate = resp.GetCreationDate()
response.Target.SigningKey = resp.GetSigningKey()
return nil
},
req: &action.GetTargetRequest{},
},
want: &action.GetTargetResponse{
Target: &action.Target{
Endpoint: "https://example.com",
TargetType: &action.Target_RestCall{
RestCall: &action.RESTCall{
InterruptOnError: false,
},
},
Timeout: durationpb.New(5 * time.Second),
},
},
},
{
name: "get, call interruptOnError, ok",
args: args{
ctx: isolatedIAMOwnerCTX,
dep: func(ctx context.Context, request *action.GetTargetRequest, response *action.GetTargetResponse) error {
name := gofakeit.Name()
resp := instance.CreateTarget(ctx, t, name, "https://example.com", domain.TargetTypeCall, true)
request.Id = resp.GetId()
response.Target.Id = resp.GetId()
response.Target.Name = name
response.Target.CreationDate = resp.GetCreationDate()
response.Target.ChangeDate = resp.GetCreationDate()
response.Target.SigningKey = resp.GetSigningKey()
return nil
},
req: &action.GetTargetRequest{},
},
want: &action.GetTargetResponse{
Target: &action.Target{
Endpoint: "https://example.com",
TargetType: &action.Target_RestCall{
RestCall: &action.RESTCall{
InterruptOnError: true,
},
},
Timeout: durationpb.New(5 * time.Second),
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.args.dep != nil {
err := tt.args.dep(tt.args.ctx, tt.args.req, tt.want)
require.NoError(t, err)
}
retryDuration, tick := integration.WaitForAndTickWithMaxDuration(isolatedIAMOwnerCTX, 2*time.Minute)
require.EventuallyWithT(t, func(ttt *assert.CollectT) {
got, err := instance.Client.ActionV2beta.GetTarget(tt.args.ctx, tt.args.req)
if tt.wantErr {
assert.Error(ttt, err, "Error: "+err.Error())
return
}
assert.NoError(ttt, err)
assert.EqualExportedValues(ttt, tt.want, got)
}, retryDuration, tick, "timeout waiting for expected target result")
})
}
}
func TestServer_ListTargets(t *testing.T) {
instance := integration.NewInstance(CTX)
isolatedIAMOwnerCTX := instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
type args struct {
ctx context.Context
dep func(context.Context, *action.ListTargetsRequest, *action.ListTargetsResponse)
req *action.ListTargetsRequest
}
tests := []struct {
name string
args args
want *action.ListTargetsResponse
wantErr bool
}{
{
name: "missing permission",
args: args{
ctx: instance.WithAuthorization(context.Background(), integration.UserTypeOrgOwner),
req: &action.ListTargetsRequest{},
},
wantErr: true,
},
{
name: "list, not found",
args: args{
ctx: isolatedIAMOwnerCTX,
req: &action.ListTargetsRequest{
Filters: []*action.TargetSearchFilter{
{Filter: &action.TargetSearchFilter_InTargetIdsFilter{
InTargetIdsFilter: &action.InTargetIDsFilter{
TargetIds: []string{"notfound"},
},
},
},
},
},
},
want: &action.ListTargetsResponse{
Pagination: &filter.PaginationResponse{
TotalResult: 0,
AppliedLimit: 100,
},
Result: []*action.Target{},
},
},
{
name: "list single id",
args: args{
ctx: isolatedIAMOwnerCTX,
dep: func(ctx context.Context, request *action.ListTargetsRequest, response *action.ListTargetsResponse) {
name := gofakeit.Name()
resp := instance.CreateTarget(ctx, t, name, "https://example.com", domain.TargetTypeWebhook, false)
request.Filters[0].Filter = &action.TargetSearchFilter_InTargetIdsFilter{
InTargetIdsFilter: &action.InTargetIDsFilter{
TargetIds: []string{resp.GetId()},
},
}
response.Result[0].Id = resp.GetId()
response.Result[0].Name = name
response.Result[0].CreationDate = resp.GetCreationDate()
response.Result[0].ChangeDate = resp.GetCreationDate()
response.Result[0].SigningKey = resp.GetSigningKey()
},
req: &action.ListTargetsRequest{
Filters: []*action.TargetSearchFilter{{}},
},
},
want: &action.ListTargetsResponse{
Pagination: &filter.PaginationResponse{
TotalResult: 1,
AppliedLimit: 100,
},
Result: []*action.Target{
{
Endpoint: "https://example.com",
TargetType: &action.Target_RestWebhook{
RestWebhook: &action.RESTWebhook{
InterruptOnError: false,
},
},
Timeout: durationpb.New(5 * time.Second),
},
},
},
}, {
name: "list single name",
args: args{
ctx: isolatedIAMOwnerCTX,
dep: func(ctx context.Context, request *action.ListTargetsRequest, response *action.ListTargetsResponse) {
name := gofakeit.Name()
resp := instance.CreateTarget(ctx, t, name, "https://example.com", domain.TargetTypeWebhook, false)
request.Filters[0].Filter = &action.TargetSearchFilter_TargetNameFilter{
TargetNameFilter: &action.TargetNameFilter{
TargetName: name,
},
}
response.Result[0].Id = resp.GetId()
response.Result[0].Name = name
response.Result[0].CreationDate = resp.GetCreationDate()
response.Result[0].ChangeDate = resp.GetCreationDate()
response.Result[0].SigningKey = resp.GetSigningKey()
},
req: &action.ListTargetsRequest{
Filters: []*action.TargetSearchFilter{{}},
},
},
want: &action.ListTargetsResponse{
Pagination: &filter.PaginationResponse{
TotalResult: 1,
AppliedLimit: 100,
},
Result: []*action.Target{
{
Endpoint: "https://example.com",
TargetType: &action.Target_RestWebhook{
RestWebhook: &action.RESTWebhook{
InterruptOnError: false,
},
},
Timeout: durationpb.New(5 * time.Second),
},
},
},
},
{
name: "list multiple id",
args: args{
ctx: isolatedIAMOwnerCTX,
dep: func(ctx context.Context, request *action.ListTargetsRequest, response *action.ListTargetsResponse) {
name1 := gofakeit.Name()
name2 := gofakeit.Name()
name3 := gofakeit.Name()
resp1 := instance.CreateTarget(ctx, t, name1, "https://example.com", domain.TargetTypeWebhook, false)
resp2 := instance.CreateTarget(ctx, t, name2, "https://example.com", domain.TargetTypeCall, true)
resp3 := instance.CreateTarget(ctx, t, name3, "https://example.com", domain.TargetTypeAsync, false)
request.Filters[0].Filter = &action.TargetSearchFilter_InTargetIdsFilter{
InTargetIdsFilter: &action.InTargetIDsFilter{
TargetIds: []string{resp1.GetId(), resp2.GetId(), resp3.GetId()},
},
}
response.Result[2].Id = resp1.GetId()
response.Result[2].Name = name1
response.Result[2].CreationDate = resp1.GetCreationDate()
response.Result[2].ChangeDate = resp1.GetCreationDate()
response.Result[2].SigningKey = resp1.GetSigningKey()
response.Result[1].Id = resp2.GetId()
response.Result[1].Name = name2
response.Result[1].CreationDate = resp2.GetCreationDate()
response.Result[1].ChangeDate = resp2.GetCreationDate()
response.Result[1].SigningKey = resp2.GetSigningKey()
response.Result[0].Id = resp3.GetId()
response.Result[0].Name = name3
response.Result[0].CreationDate = resp3.GetCreationDate()
response.Result[0].ChangeDate = resp3.GetCreationDate()
response.Result[0].SigningKey = resp3.GetSigningKey()
},
req: &action.ListTargetsRequest{
Filters: []*action.TargetSearchFilter{{}},
},
},
want: &action.ListTargetsResponse{
Pagination: &filter.PaginationResponse{
TotalResult: 3,
AppliedLimit: 100,
},
Result: []*action.Target{
{
Endpoint: "https://example.com",
TargetType: &action.Target_RestAsync{
RestAsync: &action.RESTAsync{},
},
Timeout: durationpb.New(5 * time.Second),
},
{
Endpoint: "https://example.com",
TargetType: &action.Target_RestCall{
RestCall: &action.RESTCall{
InterruptOnError: true,
},
},
Timeout: durationpb.New(5 * time.Second),
},
{
Endpoint: "https://example.com",
TargetType: &action.Target_RestWebhook{
RestWebhook: &action.RESTWebhook{
InterruptOnError: false,
},
},
Timeout: durationpb.New(5 * time.Second),
},
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.args.dep != nil {
tt.args.dep(tt.args.ctx, tt.args.req, tt.want)
}
retryDuration, tick := integration.WaitForAndTickWithMaxDuration(isolatedIAMOwnerCTX, time.Minute)
require.EventuallyWithT(t, func(ttt *assert.CollectT) {
got, listErr := instance.Client.ActionV2beta.ListTargets(tt.args.ctx, tt.args.req)
if tt.wantErr {
require.Error(ttt, listErr, "Error: "+listErr.Error())
return
}
require.NoError(ttt, listErr)
// always first check length, otherwise its failed anyway
if assert.Len(ttt, got.Result, len(tt.want.Result)) {
for i := range tt.want.Result {
assert.EqualExportedValues(ttt, tt.want.Result[i], got.Result[i])
}
}
assertPaginationResponse(ttt, tt.want.Pagination, got.Pagination)
}, retryDuration, tick, "timeout waiting for expected execution result")
})
}
}
func assertPaginationResponse(t *assert.CollectT, expected *filter.PaginationResponse, actual *filter.PaginationResponse) {
assert.Equal(t, expected.AppliedLimit, actual.AppliedLimit)
assert.Equal(t, expected.TotalResult, actual.TotalResult)
}
func TestServer_ListExecutions(t *testing.T) {
instance := integration.NewInstance(CTX)
isolatedIAMOwnerCTX := instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
targetResp := instance.CreateTarget(isolatedIAMOwnerCTX, t, "", "https://example.com", domain.TargetTypeWebhook, false)
type args struct {
ctx context.Context
dep func(context.Context, *action.ListExecutionsRequest, *action.ListExecutionsResponse)
req *action.ListExecutionsRequest
}
tests := []struct {
name string
args args
want *action.ListExecutionsResponse
wantErr bool
}{
{
name: "missing permission",
args: args{
ctx: instance.WithAuthorization(context.Background(), integration.UserTypeOrgOwner),
req: &action.ListExecutionsRequest{},
},
wantErr: true,
},
{
name: "list request single condition",
args: args{
ctx: isolatedIAMOwnerCTX,
dep: func(ctx context.Context, request *action.ListExecutionsRequest, response *action.ListExecutionsResponse) {
cond := request.Filters[0].GetInConditionsFilter().GetConditions()[0]
resp := instance.SetExecution(ctx, t, cond, []string{targetResp.GetId()})
// Set expected response with used values for SetExecution
response.Result[0].CreationDate = resp.GetSetDate()
response.Result[0].ChangeDate = resp.GetSetDate()
response.Result[0].Condition = cond
},
req: &action.ListExecutionsRequest{
Filters: []*action.ExecutionSearchFilter{{
Filter: &action.ExecutionSearchFilter_InConditionsFilter{
InConditionsFilter: &action.InConditionsFilter{
Conditions: []*action.Condition{{
ConditionType: &action.Condition_Request{
Request: &action.RequestExecution{
Condition: &action.RequestExecution_Method{
Method: "/zitadel.session.v2.SessionService/GetSession",
},
},
},
}},
},
},
}},
},
},
want: &action.ListExecutionsResponse{
Pagination: &filter.PaginationResponse{
TotalResult: 1,
AppliedLimit: 100,
},
Result: []*action.Execution{
{
Condition: &action.Condition{
ConditionType: &action.Condition_Request{
Request: &action.RequestExecution{
Condition: &action.RequestExecution_Method{
Method: "/zitadel.session.v2.SessionService/GetSession",
},
},
},
},
Targets: []string{targetResp.GetId()},
},
},
},
},
{
name: "list request single target",
args: args{
ctx: isolatedIAMOwnerCTX,
dep: func(ctx context.Context, request *action.ListExecutionsRequest, response *action.ListExecutionsResponse) {
target := instance.CreateTarget(isolatedIAMOwnerCTX, t, "", "https://example.com", domain.TargetTypeWebhook, false)
// add target as Filter to the request
request.Filters[0] = &action.ExecutionSearchFilter{
Filter: &action.ExecutionSearchFilter_TargetFilter{
TargetFilter: &action.TargetFilter{
TargetId: target.GetId(),
},
},
}
cond := &action.Condition{
ConditionType: &action.Condition_Request{
Request: &action.RequestExecution{
Condition: &action.RequestExecution_Method{
Method: "/zitadel.management.v1.ManagementService/UpdateAction",
},
},
},
}
resp := instance.SetExecution(ctx, t, cond, []string{target.GetId()})
response.Result[0].CreationDate = resp.GetSetDate()
response.Result[0].ChangeDate = resp.GetSetDate()
response.Result[0].Condition = cond
response.Result[0].Targets = []string{target.GetId()}
},
req: &action.ListExecutionsRequest{
Filters: []*action.ExecutionSearchFilter{{}},
},
},
want: &action.ListExecutionsResponse{
Pagination: &filter.PaginationResponse{
TotalResult: 1,
AppliedLimit: 100,
},
Result: []*action.Execution{
{
Condition: &action.Condition{},
Targets: []string{""},
},
},
},
},
{
name: "list multiple conditions",
args: args{
ctx: isolatedIAMOwnerCTX,
dep: func(ctx context.Context, request *action.ListExecutionsRequest, response *action.ListExecutionsResponse) {
request.Filters[0] = &action.ExecutionSearchFilter{
Filter: &action.ExecutionSearchFilter_InConditionsFilter{
InConditionsFilter: &action.InConditionsFilter{
Conditions: []*action.Condition{
{ConditionType: &action.Condition_Request{
Request: &action.RequestExecution{
Condition: &action.RequestExecution_Method{
Method: "/zitadel.session.v2.SessionService/GetSession",
},
},
}},
{ConditionType: &action.Condition_Request{
Request: &action.RequestExecution{
Condition: &action.RequestExecution_Method{
Method: "/zitadel.session.v2.SessionService/CreateSession",
},
},
}},
{ConditionType: &action.Condition_Request{
Request: &action.RequestExecution{
Condition: &action.RequestExecution_Method{
Method: "/zitadel.session.v2.SessionService/SetSession",
},
},
}},
},
},
},
}
cond1 := request.Filters[0].GetInConditionsFilter().GetConditions()[0]
resp1 := instance.SetExecution(ctx, t, cond1, []string{targetResp.GetId()})
response.Result[2] = &action.Execution{
CreationDate: resp1.GetSetDate(),
ChangeDate: resp1.GetSetDate(),
Condition: cond1,
Targets: []string{targetResp.GetId()},
}
cond2 := request.Filters[0].GetInConditionsFilter().GetConditions()[1]
resp2 := instance.SetExecution(ctx, t, cond2, []string{targetResp.GetId()})
response.Result[1] = &action.Execution{
CreationDate: resp2.GetSetDate(),
ChangeDate: resp2.GetSetDate(),
Condition: cond2,
Targets: []string{targetResp.GetId()},
}
cond3 := request.Filters[0].GetInConditionsFilter().GetConditions()[2]
resp3 := instance.SetExecution(ctx, t, cond3, []string{targetResp.GetId()})
response.Result[0] = &action.Execution{
CreationDate: resp3.GetSetDate(),
ChangeDate: resp3.GetSetDate(),
Condition: cond3,
Targets: []string{targetResp.GetId()},
}
},
req: &action.ListExecutionsRequest{
Filters: []*action.ExecutionSearchFilter{
{},
},
},
},
want: &action.ListExecutionsResponse{
Pagination: &filter.PaginationResponse{
TotalResult: 3,
AppliedLimit: 100,
},
Result: []*action.Execution{
{}, {}, {},
},
},
},
{
name: "list multiple conditions all types",
args: args{
ctx: isolatedIAMOwnerCTX,
dep: func(ctx context.Context, request *action.ListExecutionsRequest, response *action.ListExecutionsResponse) {
conditions := request.Filters[0].GetInConditionsFilter().GetConditions()
for i, cond := range conditions {
resp := instance.SetExecution(ctx, t, cond, []string{targetResp.GetId()})
response.Result[(len(conditions)-1)-i] = &action.Execution{
CreationDate: resp.GetSetDate(),
ChangeDate: resp.GetSetDate(),
Condition: cond,
Targets: []string{targetResp.GetId()},
}
}
},
req: &action.ListExecutionsRequest{
Filters: []*action.ExecutionSearchFilter{{
Filter: &action.ExecutionSearchFilter_InConditionsFilter{
InConditionsFilter: &action.InConditionsFilter{
Conditions: []*action.Condition{
{ConditionType: &action.Condition_Request{Request: &action.RequestExecution{Condition: &action.RequestExecution_Method{Method: "/zitadel.session.v2.SessionService/GetSession"}}}},
{ConditionType: &action.Condition_Request{Request: &action.RequestExecution{Condition: &action.RequestExecution_Service{Service: "zitadel.session.v2.SessionService"}}}},
{ConditionType: &action.Condition_Request{Request: &action.RequestExecution{Condition: &action.RequestExecution_All{All: true}}}},
{ConditionType: &action.Condition_Response{Response: &action.ResponseExecution{Condition: &action.ResponseExecution_Method{Method: "/zitadel.session.v2.SessionService/GetSession"}}}},
{ConditionType: &action.Condition_Response{Response: &action.ResponseExecution{Condition: &action.ResponseExecution_Service{Service: "zitadel.session.v2.SessionService"}}}},
{ConditionType: &action.Condition_Response{Response: &action.ResponseExecution{Condition: &action.ResponseExecution_All{All: true}}}},
{ConditionType: &action.Condition_Event{Event: &action.EventExecution{Condition: &action.EventExecution_Event{Event: "user.added"}}}},
{ConditionType: &action.Condition_Event{Event: &action.EventExecution{Condition: &action.EventExecution_Group{Group: "user"}}}},
{ConditionType: &action.Condition_Event{Event: &action.EventExecution{Condition: &action.EventExecution_All{All: true}}}},
{ConditionType: &action.Condition_Function{Function: &action.FunctionExecution{Name: "presamlresponse"}}},
},
},
},
}},
},
},
want: &action.ListExecutionsResponse{
Pagination: &filter.PaginationResponse{
TotalResult: 10,
AppliedLimit: 100,
},
Result: []*action.Execution{
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
},
},
},
{
name: "list multiple conditions all types, sort id",
args: args{
ctx: isolatedIAMOwnerCTX,
dep: func(ctx context.Context, request *action.ListExecutionsRequest, response *action.ListExecutionsResponse) {
conditions := request.Filters[0].GetInConditionsFilter().GetConditions()
for i, cond := range conditions {
resp := instance.SetExecution(ctx, t, cond, []string{targetResp.GetId()})
response.Result[i] = &action.Execution{
CreationDate: resp.GetSetDate(),
ChangeDate: resp.GetSetDate(),
Condition: cond,
Targets: []string{targetResp.GetId()},
}
}
},
req: &action.ListExecutionsRequest{
SortingColumn: gu.Ptr(action.ExecutionFieldName_EXECUTION_FIELD_NAME_ID),
Filters: []*action.ExecutionSearchFilter{{
Filter: &action.ExecutionSearchFilter_InConditionsFilter{
InConditionsFilter: &action.InConditionsFilter{
Conditions: []*action.Condition{
{ConditionType: &action.Condition_Response{Response: &action.ResponseExecution{Condition: &action.ResponseExecution_Method{Method: "/zitadel.session.v2.SessionService/GetSession"}}}},
{ConditionType: &action.Condition_Response{Response: &action.ResponseExecution{Condition: &action.ResponseExecution_Service{Service: "zitadel.session.v2.SessionService"}}}},
{ConditionType: &action.Condition_Response{Response: &action.ResponseExecution{Condition: &action.ResponseExecution_All{All: true}}}},
{ConditionType: &action.Condition_Request{Request: &action.RequestExecution{Condition: &action.RequestExecution_Method{Method: "/zitadel.session.v2.SessionService/GetSession"}}}},
{ConditionType: &action.Condition_Request{Request: &action.RequestExecution{Condition: &action.RequestExecution_Service{Service: "zitadel.session.v2.SessionService"}}}},
{ConditionType: &action.Condition_Request{Request: &action.RequestExecution{Condition: &action.RequestExecution_All{All: true}}}},
{ConditionType: &action.Condition_Function{Function: &action.FunctionExecution{Name: "presamlresponse"}}},
{ConditionType: &action.Condition_Event{Event: &action.EventExecution{Condition: &action.EventExecution_Event{Event: "user.added"}}}},
{ConditionType: &action.Condition_Event{Event: &action.EventExecution{Condition: &action.EventExecution_Group{Group: "user"}}}},
{ConditionType: &action.Condition_Event{Event: &action.EventExecution{Condition: &action.EventExecution_All{All: true}}}},
},
},
},
}},
},
},
want: &action.ListExecutionsResponse{
Pagination: &filter.PaginationResponse{
TotalResult: 10,
AppliedLimit: 100,
},
Result: []*action.Execution{
{},
{},
{},
{},
{},
{},
{},
{},
{},
{},
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.args.dep != nil {
tt.args.dep(tt.args.ctx, tt.args.req, tt.want)
}
retryDuration, tick := integration.WaitForAndTickWithMaxDuration(isolatedIAMOwnerCTX, time.Minute)
require.EventuallyWithT(t, func(ttt *assert.CollectT) {
got, listErr := instance.Client.ActionV2beta.ListExecutions(tt.args.ctx, tt.args.req)
if tt.wantErr {
require.Error(ttt, listErr, "Error: "+listErr.Error())
return
}
require.NoError(ttt, listErr)
// always first check length, otherwise its failed anyway
if assert.Len(ttt, got.Result, len(tt.want.Result)) {
assert.EqualExportedValues(ttt, got.Result, tt.want.Result)
}
assertPaginationResponse(ttt, tt.want.Pagination, got.Pagination)
}, retryDuration, tick, "timeout waiting for expected execution result")
})
}
}