fix: remove action feature flag and include execution (#9727)

# Which Problems Are Solved

Actions v2 is not a feature flag anymore, include functionality on
executions is not used and json tags of proto messages are handled
incorrectly.

# How the Problems Are Solved

- Remove actions from the feature flags on system and instance level
- Remove include type on executions, only in the API, later maybe in the
handling logic as well
- Use protojson in request and response handling of actions v2

# Additional Changes

- Correct integration tests for request and response handling
- Use json.RawMessage for events, so that the event payload is not
base64 encoded
- Added separate context for async webhook calls, that executions are
not cancelled when called async

# Additional Context

Related to #9759 
Closes #9710

---------

Co-authored-by: Livio Spring <livio.a@gmail.com>
This commit is contained in:
Stefan Benz
2025-04-28 11:24:50 +02:00
committed by GitHub
parent 84628671bd
commit b8ba7bd5ba
55 changed files with 427 additions and 799 deletions

View File

@@ -14,22 +14,10 @@ import (
)
func (s *Server) SetExecution(ctx context.Context, req *action.SetExecutionRequest) (*action.SetExecutionResponse, error) {
if err := checkActionsEnabled(ctx); err != nil {
return nil, err
}
reqTargets := req.GetTargets()
targets := make([]*execution.Target, len(reqTargets))
for i, target := range reqTargets {
switch t := target.GetType().(type) {
case *action.ExecutionTargetType_Include:
include, err := conditionToInclude(t.Include)
if err != nil {
return nil, err
}
targets[i] = &execution.Target{Type: domain.ExecutionTargetTypeInclude, Target: include}
case *action.ExecutionTargetType_Target:
targets[i] = &execution.Target{Type: domain.ExecutionTargetTypeTarget, Target: t.Target}
}
targets[i] = &execution.Target{Type: domain.ExecutionTargetTypeTarget, Target: target}
}
set := &command.SetExecution{
Targets: targets,
@@ -60,59 +48,19 @@ func (s *Server) SetExecution(ctx context.Context, req *action.SetExecutionReque
}, nil
}
func conditionToInclude(cond *action.Condition) (string, error) {
switch t := cond.GetConditionType().(type) {
case *action.Condition_Request:
cond := executionConditionFromRequest(t.Request)
if err := cond.IsValid(); err != nil {
return "", err
}
return cond.ID(domain.ExecutionTypeRequest), nil
case *action.Condition_Response:
cond := executionConditionFromResponse(t.Response)
if err := cond.IsValid(); err != nil {
return "", err
}
return cond.ID(domain.ExecutionTypeRequest), nil
case *action.Condition_Event:
cond := executionConditionFromEvent(t.Event)
if err := cond.IsValid(); err != nil {
return "", err
}
return cond.ID(), nil
case *action.Condition_Function:
cond := command.ExecutionFunctionCondition(t.Function.GetName())
if err := cond.IsValid(); err != nil {
return "", err
}
return cond.ID(), nil
default:
return "", zerrors.ThrowInvalidArgument(nil, "ACTION-9BBob", "Errors.Execution.ConditionInvalid")
}
}
func (s *Server) ListExecutionFunctions(ctx context.Context, _ *action.ListExecutionFunctionsRequest) (*action.ListExecutionFunctionsResponse, error) {
if err := checkActionsEnabled(ctx); err != nil {
return nil, err
}
return &action.ListExecutionFunctionsResponse{
Functions: s.ListActionFunctions(),
}, nil
}
func (s *Server) ListExecutionMethods(ctx context.Context, _ *action.ListExecutionMethodsRequest) (*action.ListExecutionMethodsResponse, error) {
if err := checkActionsEnabled(ctx); err != nil {
return nil, err
}
return &action.ListExecutionMethodsResponse{
Methods: s.ListGRPCMethods(),
}, nil
}
func (s *Server) ListExecutionServices(ctx context.Context, _ *action.ListExecutionServicesRequest) (*action.ListExecutionServicesResponse, error) {
if err := checkActionsEnabled(ctx); err != nil {
return nil, err
}
return &action.ListExecutionServicesResponse{
Services: s.ListGRPCServices(),
}, nil

View File

@@ -48,7 +48,6 @@ var (
func TestServer_ExecutionTarget(t *testing.T) {
instance := integration.NewInstance(CTX)
ensureFeatureEnabled(t, instance)
isolatedIAMOwnerCTX := instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
fullMethod := action.ActionService_GetTarget_FullMethodName
@@ -77,14 +76,14 @@ func TestServer_ExecutionTarget(t *testing.T) {
targetCreated := instance.CreateTarget(ctx, t, targetCreatedName, targetCreatedURL, domain.TargetTypeCall, false)
// request received by target
wantRequest := &middleware.ContextInfoRequest{FullMethod: fullMethod, InstanceID: instance.ID(), OrgID: orgID, ProjectID: projectID, UserID: userID, Request: request}
wantRequest := &middleware.ContextInfoRequest{FullMethod: fullMethod, InstanceID: instance.ID(), OrgID: orgID, ProjectID: projectID, UserID: userID, Request: middleware.Message{Message: request}}
changedRequest := &action.GetTargetRequest{Id: targetCreated.GetId()}
// replace original request with different targetID
urlRequest, closeRequest, calledRequest, _ := integration.TestServerCall(wantRequest, 0, http.StatusOK, changedRequest)
urlRequest, closeRequest, calledRequest, _ := integration.TestServerCallProto(wantRequest, 0, http.StatusOK, changedRequest)
targetRequest := waitForTarget(ctx, t, instance, urlRequest, domain.TargetTypeCall, false)
waitForExecutionOnCondition(ctx, t, instance, conditionRequestFullMethod(fullMethod), executionTargetsSingleTarget(targetRequest.GetId()))
waitForExecutionOnCondition(ctx, t, instance, conditionRequestFullMethod(fullMethod), []string{targetRequest.GetId()})
// expected response from the GetTarget
expectedResponse := &action.GetTargetResponse{
@@ -103,9 +102,26 @@ func TestServer_ExecutionTarget(t *testing.T) {
SigningKey: targetCreated.GetSigningKey(),
},
}
// has to be set separately because of the pointers
changedResponse := &action.GetTargetResponse{
Target: &action.Target{
Id: "changed",
CreationDate: targetCreated.GetCreationDate(),
ChangeDate: targetCreated.GetCreationDate(),
Name: targetCreatedName,
TargetType: &action.Target_RestCall{
RestCall: &action.RESTCall{
InterruptOnError: false,
},
},
Timeout: durationpb.New(5 * time.Second),
Endpoint: targetCreatedURL,
SigningKey: targetCreated.GetSigningKey(),
},
}
// content for update
response.Target = &action.Target{
Id: targetCreated.GetId(),
Id: "changed",
CreationDate: targetCreated.GetCreationDate(),
ChangeDate: targetCreated.GetCreationDate(),
Name: targetCreatedName,
@@ -119,18 +135,6 @@ func TestServer_ExecutionTarget(t *testing.T) {
SigningKey: targetCreated.GetSigningKey(),
}
// content for partial update
changedResponse := &action.GetTargetResponse{
Target: &action.Target{
Id: targetCreated.GetId(),
TargetType: &action.Target_RestCall{
RestCall: &action.RESTCall{
InterruptOnError: false,
},
},
},
}
// response received by target
wantResponse := &middleware.ContextInfoResponse{
FullMethod: fullMethod,
@@ -138,14 +142,14 @@ func TestServer_ExecutionTarget(t *testing.T) {
OrgID: orgID,
ProjectID: projectID,
UserID: userID,
Request: changedRequest,
Response: expectedResponse,
Request: middleware.Message{Message: changedRequest},
Response: middleware.Message{Message: expectedResponse},
}
// after request with different targetID, return changed response
targetResponseURL, closeResponse, calledResponse, _ := integration.TestServerCall(wantResponse, 0, http.StatusOK, changedResponse)
targetResponseURL, closeResponse, calledResponse, _ := integration.TestServerCallProto(wantResponse, 0, http.StatusOK, changedResponse)
targetResponse := waitForTarget(ctx, t, instance, targetResponseURL, domain.TargetTypeCall, false)
waitForExecutionOnCondition(ctx, t, instance, conditionResponseFullMethod(fullMethod), executionTargetsSingleTarget(targetResponse.GetId()))
waitForExecutionOnCondition(ctx, t, instance, conditionResponseFullMethod(fullMethod), []string{targetResponse.GetId()})
return func() {
closeRequest()
closeResponse()
@@ -167,9 +171,7 @@ func TestServer_ExecutionTarget(t *testing.T) {
Id: "something",
},
want: &action.GetTargetResponse{
Target: &action.Target{
Id: "changed",
},
// defined in the dependency function
},
},
{
@@ -181,11 +183,11 @@ func TestServer_ExecutionTarget(t *testing.T) {
userID := instance.Users.Get(integration.UserTypeIAMOwner).ID
// request received by target
wantRequest := &middleware.ContextInfoRequest{FullMethod: fullMethod, InstanceID: instance.ID(), OrgID: orgID, ProjectID: projectID, UserID: userID, Request: request}
urlRequest, closeRequest, calledRequest, _ := integration.TestServerCall(wantRequest, 0, http.StatusInternalServerError, &action.GetTargetRequest{Id: "notchanged"})
wantRequest := &middleware.ContextInfoRequest{FullMethod: fullMethod, InstanceID: instance.ID(), OrgID: orgID, ProjectID: projectID, UserID: userID, Request: middleware.Message{Message: request}}
urlRequest, closeRequest, calledRequest, _ := integration.TestServerCallProto(wantRequest, 0, http.StatusInternalServerError, nil)
targetRequest := waitForTarget(ctx, t, instance, urlRequest, domain.TargetTypeCall, true)
waitForExecutionOnCondition(ctx, t, instance, conditionRequestFullMethod(fullMethod), executionTargetsSingleTarget(targetRequest.GetId()))
waitForExecutionOnCondition(ctx, t, instance, conditionRequestFullMethod(fullMethod), []string{targetRequest.GetId()})
// GetTarget with used target
request.Id = targetRequest.GetId()
return func() {
@@ -234,17 +236,6 @@ func TestServer_ExecutionTarget(t *testing.T) {
SigningKey: targetCreated.GetSigningKey(),
},
}
// content for partial update
changedResponse := &action.GetTargetResponse{
Target: &action.Target{
Id: "changed",
TargetType: &action.Target_RestCall{
RestCall: &action.RESTCall{
InterruptOnError: false,
},
},
},
}
// response received by target
wantResponse := &middleware.ContextInfoResponse{
@@ -253,14 +244,14 @@ func TestServer_ExecutionTarget(t *testing.T) {
OrgID: orgID,
ProjectID: projectID,
UserID: userID,
Request: request,
Response: expectedResponse,
Request: middleware.Message{Message: request},
Response: middleware.Message{Message: expectedResponse},
}
// after request with different targetID, return changed response
targetResponseURL, closeResponse, calledResponse, _ := integration.TestServerCall(wantResponse, 0, http.StatusInternalServerError, changedResponse)
targetResponseURL, closeResponse, calledResponse, _ := integration.TestServerCallProto(wantResponse, 0, http.StatusInternalServerError, nil)
targetResponse := waitForTarget(ctx, t, instance, targetResponseURL, domain.TargetTypeCall, true)
waitForExecutionOnCondition(ctx, t, instance, conditionResponseFullMethod(fullMethod), executionTargetsSingleTarget(targetResponse.GetId()))
waitForExecutionOnCondition(ctx, t, instance, conditionResponseFullMethod(fullMethod), []string{targetResponse.GetId()})
return func() {
closeResponse()
}, func() bool {
@@ -301,7 +292,6 @@ func TestServer_ExecutionTarget(t *testing.T) {
func TestServer_ExecutionTarget_Event(t *testing.T) {
instance := integration.NewInstance(CTX)
ensureFeatureEnabled(t, instance)
isolatedIAMOwnerCTX := instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
event := "session.added"
@@ -309,7 +299,7 @@ func TestServer_ExecutionTarget_Event(t *testing.T) {
defer closeF()
targetResponse := waitForTarget(isolatedIAMOwnerCTX, t, instance, urlRequest, domain.TargetTypeWebhook, true)
waitForExecutionOnCondition(isolatedIAMOwnerCTX, t, instance, conditionEvent(event), executionTargetsSingleTarget(targetResponse.GetId()))
waitForExecutionOnCondition(isolatedIAMOwnerCTX, t, instance, conditionEvent(event), []string{targetResponse.GetId()})
tests := []struct {
name string
@@ -359,7 +349,6 @@ func TestServer_ExecutionTarget_Event(t *testing.T) {
func TestServer_ExecutionTarget_Event_LongerThanTargetTimeout(t *testing.T) {
instance := integration.NewInstance(CTX)
ensureFeatureEnabled(t, instance)
isolatedIAMOwnerCTX := instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
event := "session.added"
@@ -368,7 +357,7 @@ func TestServer_ExecutionTarget_Event_LongerThanTargetTimeout(t *testing.T) {
defer closeF()
targetResponse := waitForTarget(isolatedIAMOwnerCTX, t, instance, urlRequest, domain.TargetTypeWebhook, true)
waitForExecutionOnCondition(isolatedIAMOwnerCTX, t, instance, conditionEvent(event), executionTargetsSingleTarget(targetResponse.GetId()))
waitForExecutionOnCondition(isolatedIAMOwnerCTX, t, instance, conditionEvent(event), []string{targetResponse.GetId()})
tests := []struct {
name string
@@ -412,7 +401,6 @@ func TestServer_ExecutionTarget_Event_LongerThanTargetTimeout(t *testing.T) {
func TestServer_ExecutionTarget_Event_LongerThanTransactionTimeout(t *testing.T) {
instance := integration.NewInstance(CTX)
ensureFeatureEnabled(t, instance)
isolatedIAMOwnerCTX := instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
event := "session.added"
@@ -420,7 +408,7 @@ func TestServer_ExecutionTarget_Event_LongerThanTransactionTimeout(t *testing.T)
defer closeF()
targetResponse := waitForTarget(isolatedIAMOwnerCTX, t, instance, urlRequest, domain.TargetTypeWebhook, true)
waitForExecutionOnCondition(isolatedIAMOwnerCTX, t, instance, conditionEvent(event), executionTargetsSingleTarget(targetResponse.GetId()))
waitForExecutionOnCondition(isolatedIAMOwnerCTX, t, instance, conditionEvent(event), []string{targetResponse.GetId()})
tests := []struct {
name string
@@ -474,7 +462,7 @@ func TestServer_ExecutionTarget_Event_LongerThanTransactionTimeout(t *testing.T)
}
}
func waitForExecutionOnCondition(ctx context.Context, t *testing.T, instance *integration.Instance, condition *action.Condition, targets []*action.ExecutionTargetType) {
func waitForExecutionOnCondition(ctx context.Context, t *testing.T, instance *integration.Instance, condition *action.Condition, targets []string) {
instance.SetExecution(ctx, t, condition, targets)
retryDuration, tick := integration.WaitForAndTickWithMaxDuration(ctx, time.Minute)
@@ -496,7 +484,7 @@ func waitForExecutionOnCondition(ctx context.Context, t *testing.T, instance *in
// always first check length, otherwise its failed anyway
if assert.Len(ttt, gotTargets, len(targets)) {
for i := range targets {
assert.EqualExportedValues(ttt, targets[i].GetType(), gotTargets[i].GetType())
assert.EqualExportedValues(ttt, targets[i], gotTargets[i])
}
}
}, retryDuration, tick, "timeout waiting for expected execution result")
@@ -589,7 +577,6 @@ func conditionFunction(function string) *action.Condition {
func TestServer_ExecutionTargetPreUserinfo(t *testing.T) {
instance := integration.NewInstance(CTX)
ensureFeatureEnabled(t, instance)
isolatedIAMCtx := instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
ctxLoginClient := instance.WithAuthorization(CTX, integration.UserTypeLogin)
@@ -785,7 +772,7 @@ func expectPreUserinfoExecution(ctx context.Context, t *testing.T, instance *int
targetURL, closeF, _, _ := integration.TestServerCall(expectedContextInfo, 0, http.StatusOK, response)
targetResp := waitForTarget(ctx, t, instance, targetURL, domain.TargetTypeCall, true)
waitForExecutionOnCondition(ctx, t, instance, conditionFunction("preuserinfo"), executionTargetsSingleTarget(targetResp.GetId()))
waitForExecutionOnCondition(ctx, t, instance, conditionFunction("preuserinfo"), []string{targetResp.GetId()})
return userResp.GetUserId(), closeF
}
@@ -903,7 +890,6 @@ func contextInfoForUserOIDC(instance *integration.Instance, function string, use
func TestServer_ExecutionTargetPreAccessToken(t *testing.T) {
instance := integration.NewInstance(CTX)
ensureFeatureEnabled(t, instance)
isolatedIAMCtx := instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
ctxLoginClient := instance.WithAuthorization(CTX, integration.UserTypeLogin)
@@ -1091,13 +1077,12 @@ func expectPreAccessTokenExecution(ctx context.Context, t *testing.T, instance *
targetURL, closeF, _, _ := integration.TestServerCall(expectedContextInfo, 0, http.StatusOK, response)
targetResp := waitForTarget(ctx, t, instance, targetURL, domain.TargetTypeCall, true)
waitForExecutionOnCondition(ctx, t, instance, conditionFunction("preaccesstoken"), executionTargetsSingleTarget(targetResp.GetId()))
waitForExecutionOnCondition(ctx, t, instance, conditionFunction("preaccesstoken"), []string{targetResp.GetId()})
return userResp.GetUserId(), closeF
}
func TestServer_ExecutionTargetPreSAMLResponse(t *testing.T) {
instance := integration.NewInstance(CTX)
ensureFeatureEnabled(t, instance)
isolatedIAMCtx := instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
ctxLoginClient := instance.WithAuthorization(CTX, integration.UserTypeLogin)
@@ -1257,7 +1242,7 @@ func expectPreSAMLResponseExecution(ctx context.Context, t *testing.T, instance
targetURL, closeF, _, _ := integration.TestServerCall(expectedContextInfo, 0, http.StatusOK, response)
targetResp := waitForTarget(ctx, t, instance, targetURL, domain.TargetTypeCall, true)
waitForExecutionOnCondition(ctx, t, instance, conditionFunction("presamlresponse"), executionTargetsSingleTarget(targetResp.GetId()))
waitForExecutionOnCondition(ctx, t, instance, conditionFunction("presamlresponse"), []string{targetResp.GetId()})
return userResp.GetUserId(), closeF
}

View File

@@ -15,17 +15,8 @@ import (
action "github.com/zitadel/zitadel/pkg/grpc/action/v2beta"
)
func executionTargetsSingleTarget(id string) []*action.ExecutionTargetType {
return []*action.ExecutionTargetType{{Type: &action.ExecutionTargetType_Target{Target: id}}}
}
func executionTargetsSingleInclude(include *action.Condition) []*action.ExecutionTargetType {
return []*action.ExecutionTargetType{{Type: &action.ExecutionTargetType_Include{Include: include}}}
}
func TestServer_SetExecution_Request(t *testing.T) {
instance := integration.NewInstance(CTX)
ensureFeatureEnabled(t, instance)
isolatedIAMOwnerCTX := instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
targetResp := instance.CreateTarget(isolatedIAMOwnerCTX, t, "", "https://notexisting", domain.TargetTypeWebhook, false)
@@ -59,7 +50,7 @@ func TestServer_SetExecution_Request(t *testing.T) {
Request: &action.RequestExecution{},
},
},
Targets: executionTargetsSingleTarget(targetResp.GetId()),
Targets: []string{targetResp.GetId()},
},
wantErr: true,
},
@@ -76,7 +67,7 @@ func TestServer_SetExecution_Request(t *testing.T) {
},
},
},
Targets: executionTargetsSingleTarget(targetResp.GetId()),
Targets: []string{targetResp.GetId()},
},
wantErr: true,
},
@@ -93,7 +84,7 @@ func TestServer_SetExecution_Request(t *testing.T) {
},
},
},
Targets: executionTargetsSingleTarget(targetResp.GetId()),
Targets: []string{targetResp.GetId()},
},
wantSetDate: true,
},
@@ -110,7 +101,7 @@ func TestServer_SetExecution_Request(t *testing.T) {
},
},
},
Targets: executionTargetsSingleTarget(targetResp.GetId()),
Targets: []string{targetResp.GetId()},
},
wantErr: true,
},
@@ -127,7 +118,7 @@ func TestServer_SetExecution_Request(t *testing.T) {
},
},
},
Targets: executionTargetsSingleTarget(targetResp.GetId()),
Targets: []string{targetResp.GetId()},
},
wantSetDate: true,
},
@@ -144,7 +135,7 @@ func TestServer_SetExecution_Request(t *testing.T) {
},
},
},
Targets: executionTargetsSingleTarget(targetResp.GetId()),
Targets: []string{targetResp.GetId()},
},
wantSetDate: true,
},
@@ -181,125 +172,8 @@ func assertSetExecutionResponse(t *testing.T, creationDate, setDate time.Time, e
}
}
func TestServer_SetExecution_Request_Include(t *testing.T) {
instance := integration.NewInstance(CTX)
ensureFeatureEnabled(t, instance)
isolatedIAMOwnerCTX := instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
targetResp := instance.CreateTarget(isolatedIAMOwnerCTX, t, "", "https://notexisting", domain.TargetTypeWebhook, false)
executionCond := &action.Condition{
ConditionType: &action.Condition_Request{
Request: &action.RequestExecution{
Condition: &action.RequestExecution_All{
All: true,
},
},
},
}
instance.SetExecution(isolatedIAMOwnerCTX, t,
executionCond,
executionTargetsSingleTarget(targetResp.GetId()),
)
circularExecutionService := &action.Condition{
ConditionType: &action.Condition_Request{
Request: &action.RequestExecution{
Condition: &action.RequestExecution_Service{
Service: "zitadel.session.v2beta.SessionService",
},
},
},
}
instance.SetExecution(isolatedIAMOwnerCTX, t,
circularExecutionService,
executionTargetsSingleInclude(executionCond),
)
circularExecutionMethod := &action.Condition{
ConditionType: &action.Condition_Request{
Request: &action.RequestExecution{
Condition: &action.RequestExecution_Method{
Method: "/zitadel.session.v2beta.SessionService/ListSessions",
},
},
},
}
instance.SetExecution(isolatedIAMOwnerCTX, t,
circularExecutionMethod,
executionTargetsSingleInclude(circularExecutionService),
)
tests := []struct {
name string
ctx context.Context
req *action.SetExecutionRequest
wantSetDate bool
wantErr bool
}{
{
name: "method, circular error",
ctx: isolatedIAMOwnerCTX,
req: &action.SetExecutionRequest{
Condition: circularExecutionService,
Targets: executionTargetsSingleInclude(circularExecutionMethod),
},
wantErr: true,
},
{
name: "method, ok",
ctx: isolatedIAMOwnerCTX,
req: &action.SetExecutionRequest{
Condition: &action.Condition{
ConditionType: &action.Condition_Request{
Request: &action.RequestExecution{
Condition: &action.RequestExecution_Method{
Method: "/zitadel.session.v2beta.SessionService/ListSessions",
},
},
},
},
Targets: executionTargetsSingleInclude(executionCond),
},
wantSetDate: true,
},
{
name: "service, ok",
ctx: isolatedIAMOwnerCTX,
req: &action.SetExecutionRequest{
Condition: &action.Condition{
ConditionType: &action.Condition_Request{
Request: &action.RequestExecution{
Condition: &action.RequestExecution_Service{
Service: "zitadel.user.v2beta.UserService",
},
},
},
},
Targets: executionTargetsSingleInclude(executionCond),
},
wantSetDate: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
creationDate := time.Now().UTC()
got, err := instance.Client.ActionV2beta.SetExecution(tt.ctx, tt.req)
setDate := time.Now().UTC()
if tt.wantErr {
require.Error(t, err)
return
}
require.NoError(t, err)
assertSetExecutionResponse(t, creationDate, setDate, tt.wantSetDate, got)
// cleanup to not impact other requests
instance.DeleteExecution(tt.ctx, t, tt.req.GetCondition())
})
}
}
func TestServer_SetExecution_Response(t *testing.T) {
instance := integration.NewInstance(CTX)
ensureFeatureEnabled(t, instance)
isolatedIAMOwnerCTX := instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
targetResp := instance.CreateTarget(isolatedIAMOwnerCTX, t, "", "https://notexisting", domain.TargetTypeWebhook, false)
@@ -333,7 +207,7 @@ func TestServer_SetExecution_Response(t *testing.T) {
Response: &action.ResponseExecution{},
},
},
Targets: executionTargetsSingleTarget(targetResp.GetId()),
Targets: []string{targetResp.GetId()},
},
wantErr: true,
},
@@ -350,7 +224,7 @@ func TestServer_SetExecution_Response(t *testing.T) {
},
},
},
Targets: executionTargetsSingleTarget(targetResp.GetId()),
Targets: []string{targetResp.GetId()},
},
wantErr: true,
},
@@ -367,7 +241,7 @@ func TestServer_SetExecution_Response(t *testing.T) {
},
},
},
Targets: executionTargetsSingleTarget(targetResp.GetId()),
Targets: []string{targetResp.GetId()},
},
wantSetDate: true,
},
@@ -384,7 +258,7 @@ func TestServer_SetExecution_Response(t *testing.T) {
},
},
},
Targets: executionTargetsSingleTarget(targetResp.GetId()),
Targets: []string{targetResp.GetId()},
},
wantErr: true,
},
@@ -401,7 +275,7 @@ func TestServer_SetExecution_Response(t *testing.T) {
},
},
},
Targets: executionTargetsSingleTarget(targetResp.GetId()),
Targets: []string{targetResp.GetId()},
},
wantSetDate: true,
},
@@ -418,7 +292,7 @@ func TestServer_SetExecution_Response(t *testing.T) {
},
},
},
Targets: executionTargetsSingleTarget(targetResp.GetId()),
Targets: []string{targetResp.GetId()},
},
wantSetDate: true,
},
@@ -444,7 +318,6 @@ func TestServer_SetExecution_Response(t *testing.T) {
func TestServer_SetExecution_Event(t *testing.T) {
instance := integration.NewInstance(CTX)
ensureFeatureEnabled(t, instance)
isolatedIAMOwnerCTX := instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
targetResp := instance.CreateTarget(isolatedIAMOwnerCTX, t, "", "https://notexisting", domain.TargetTypeWebhook, false)
@@ -480,7 +353,7 @@ func TestServer_SetExecution_Event(t *testing.T) {
Event: &action.EventExecution{},
},
},
Targets: executionTargetsSingleTarget(targetResp.GetId()),
Targets: []string{targetResp.GetId()},
},
wantErr: true,
},
@@ -497,7 +370,7 @@ func TestServer_SetExecution_Event(t *testing.T) {
},
},
},
Targets: executionTargetsSingleTarget(targetResp.GetId()),
Targets: []string{targetResp.GetId()},
},
wantErr: true,
},
@@ -514,7 +387,7 @@ func TestServer_SetExecution_Event(t *testing.T) {
},
},
},
Targets: executionTargetsSingleTarget(targetResp.GetId()),
Targets: []string{targetResp.GetId()},
},
wantSetDate: true,
},
@@ -531,7 +404,7 @@ func TestServer_SetExecution_Event(t *testing.T) {
},
},
},
Targets: executionTargetsSingleTarget(targetResp.GetId()),
Targets: []string{targetResp.GetId()},
},
wantErr: true,
},
@@ -548,7 +421,7 @@ func TestServer_SetExecution_Event(t *testing.T) {
},
},
},
Targets: executionTargetsSingleTarget(targetResp.GetId()),
Targets: []string{targetResp.GetId()},
},
wantSetDate: true,
},
@@ -565,7 +438,7 @@ func TestServer_SetExecution_Event(t *testing.T) {
},
},
},
Targets: executionTargetsSingleTarget(targetResp.GetId()),
Targets: []string{targetResp.GetId()},
},
wantSetDate: true,
},
@@ -582,7 +455,7 @@ func TestServer_SetExecution_Event(t *testing.T) {
},
},
},
Targets: executionTargetsSingleTarget(targetResp.GetId()),
Targets: []string{targetResp.GetId()},
},
wantSetDate: true,
},
@@ -608,7 +481,6 @@ func TestServer_SetExecution_Event(t *testing.T) {
func TestServer_SetExecution_Function(t *testing.T) {
instance := integration.NewInstance(CTX)
ensureFeatureEnabled(t, instance)
isolatedIAMOwnerCTX := instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
targetResp := instance.CreateTarget(isolatedIAMOwnerCTX, t, "", "https://notexisting", domain.TargetTypeWebhook, false)
@@ -642,7 +514,7 @@ func TestServer_SetExecution_Function(t *testing.T) {
Response: &action.ResponseExecution{},
},
},
Targets: executionTargetsSingleTarget(targetResp.GetId()),
Targets: []string{targetResp.GetId()},
},
wantErr: true,
},
@@ -655,7 +527,7 @@ func TestServer_SetExecution_Function(t *testing.T) {
Function: &action.FunctionExecution{Name: "xxx"},
},
},
Targets: executionTargetsSingleTarget(targetResp.GetId()),
Targets: []string{targetResp.GetId()},
},
wantErr: true,
},
@@ -668,7 +540,7 @@ func TestServer_SetExecution_Function(t *testing.T) {
Function: &action.FunctionExecution{Name: "presamlresponse"},
},
},
Targets: executionTargetsSingleTarget(targetResp.GetId()),
Targets: []string{targetResp.GetId()},
},
wantSetDate: true,
},

View File

@@ -8,6 +8,7 @@ import (
"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"
@@ -20,7 +21,6 @@ import (
func TestServer_GetTarget(t *testing.T) {
instance := integration.NewInstance(CTX)
ensureFeatureEnabled(t, instance)
isolatedIAMOwnerCTX := instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
type args struct {
ctx context.Context
@@ -213,7 +213,6 @@ func TestServer_GetTarget(t *testing.T) {
func TestServer_ListTargets(t *testing.T) {
instance := integration.NewInstance(CTX)
ensureFeatureEnabled(t, instance)
isolatedIAMOwnerCTX := instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
type args struct {
ctx context.Context
@@ -446,7 +445,6 @@ func assertPaginationResponse(t *assert.CollectT, expected *filter.PaginationRes
func TestServer_ListExecutions(t *testing.T) {
instance := integration.NewInstance(CTX)
ensureFeatureEnabled(t, instance)
isolatedIAMOwnerCTX := instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
targetResp := instance.CreateTarget(isolatedIAMOwnerCTX, t, "", "https://example.com", domain.TargetTypeWebhook, false)
@@ -475,7 +473,7 @@ func TestServer_ListExecutions(t *testing.T) {
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, executionTargetsSingleTarget(targetResp.GetId()))
resp := instance.SetExecution(ctx, t, cond, []string{targetResp.GetId()})
// Set expected response with used values for SetExecution
response.Result[0].CreationDate = resp.GetSetDate()
@@ -516,7 +514,7 @@ func TestServer_ListExecutions(t *testing.T) {
},
},
},
Targets: executionTargetsSingleTarget(targetResp.GetId()),
Targets: []string{targetResp.GetId()},
},
},
},
@@ -544,13 +542,12 @@ func TestServer_ListExecutions(t *testing.T) {
},
},
}
targets := executionTargetsSingleTarget(target.GetId())
resp := instance.SetExecution(ctx, t, cond, targets)
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 = targets
response.Result[0].Targets = []string{target.GetId()}
},
req: &action.ListExecutionsRequest{
Filters: []*action.ExecutionSearchFilter{{}},
@@ -564,63 +561,10 @@ func TestServer_ListExecutions(t *testing.T) {
Result: []*action.Execution{
{
Condition: &action.Condition{},
Targets: executionTargetsSingleTarget(""),
Targets: []string{""},
},
},
},
}, {
name: "list request single include",
args: args{
ctx: isolatedIAMOwnerCTX,
dep: func(ctx context.Context, request *action.ListExecutionsRequest, response *action.ListExecutionsResponse) {
cond := &action.Condition{
ConditionType: &action.Condition_Request{
Request: &action.RequestExecution{
Condition: &action.RequestExecution_Method{
Method: "/zitadel.management.v1.ManagementService/GetAction",
},
},
},
}
instance.SetExecution(ctx, t, cond, executionTargetsSingleTarget(targetResp.GetId()))
request.Filters[0].GetIncludeFilter().Include = cond
includeCond := &action.Condition{
ConditionType: &action.Condition_Request{
Request: &action.RequestExecution{
Condition: &action.RequestExecution_Method{
Method: "/zitadel.management.v1.ManagementService/ListActions",
},
},
},
}
includeTargets := executionTargetsSingleInclude(cond)
resp2 := instance.SetExecution(ctx, t, includeCond, includeTargets)
response.Result[0] = &action.Execution{
Condition: includeCond,
CreationDate: resp2.GetSetDate(),
ChangeDate: resp2.GetSetDate(),
Targets: includeTargets,
}
},
req: &action.ListExecutionsRequest{
Filters: []*action.ExecutionSearchFilter{{
Filter: &action.ExecutionSearchFilter_IncludeFilter{
IncludeFilter: &action.IncludeFilter{},
},
}},
},
},
want: &action.ListExecutionsResponse{
Pagination: &filter.PaginationResponse{
TotalResult: 1,
AppliedLimit: 100,
},
Result: []*action.Execution{
{},
},
},
},
{
name: "list multiple conditions",
@@ -659,33 +603,30 @@ func TestServer_ListExecutions(t *testing.T) {
}
cond1 := request.Filters[0].GetInConditionsFilter().GetConditions()[0]
targets1 := executionTargetsSingleTarget(targetResp.GetId())
resp1 := instance.SetExecution(ctx, t, cond1, targets1)
resp1 := instance.SetExecution(ctx, t, cond1, []string{targetResp.GetId()})
response.Result[2] = &action.Execution{
CreationDate: resp1.GetSetDate(),
ChangeDate: resp1.GetSetDate(),
Condition: cond1,
Targets: targets1,
Targets: []string{targetResp.GetId()},
}
cond2 := request.Filters[0].GetInConditionsFilter().GetConditions()[1]
targets2 := executionTargetsSingleTarget(targetResp.GetId())
resp2 := instance.SetExecution(ctx, t, cond2, targets2)
resp2 := instance.SetExecution(ctx, t, cond2, []string{targetResp.GetId()})
response.Result[1] = &action.Execution{
CreationDate: resp2.GetSetDate(),
ChangeDate: resp2.GetSetDate(),
Condition: cond2,
Targets: targets2,
Targets: []string{targetResp.GetId()},
}
cond3 := request.Filters[0].GetInConditionsFilter().GetConditions()[2]
targets3 := executionTargetsSingleTarget(targetResp.GetId())
resp3 := instance.SetExecution(ctx, t, cond3, targets3)
resp3 := instance.SetExecution(ctx, t, cond3, []string{targetResp.GetId()})
response.Result[0] = &action.Execution{
CreationDate: resp3.GetSetDate(),
ChangeDate: resp3.GetSetDate(),
Condition: cond3,
Targets: targets3,
Targets: []string{targetResp.GetId()},
}
},
req: &action.ListExecutionsRequest{
@@ -709,15 +650,14 @@ func TestServer_ListExecutions(t *testing.T) {
args: args{
ctx: isolatedIAMOwnerCTX,
dep: func(ctx context.Context, request *action.ListExecutionsRequest, response *action.ListExecutionsResponse) {
targets := executionTargetsSingleTarget(targetResp.GetId())
conditions := request.Filters[0].GetInConditionsFilter().GetConditions()
for i, cond := range conditions {
resp := instance.SetExecution(ctx, t, cond, targets)
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: targets,
Targets: []string{targetResp.GetId()},
}
}
},
@@ -761,6 +701,63 @@ func TestServer_ListExecutions(t *testing.T) {
},
},
},
{
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) {

View File

@@ -7,14 +7,6 @@ import (
"os"
"testing"
"time"
"github.com/muhlemmer/gu"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/zitadel/zitadel/internal/integration"
action "github.com/zitadel/zitadel/pkg/grpc/action/v2beta"
"github.com/zitadel/zitadel/pkg/grpc/feature/v2"
)
var (
@@ -29,41 +21,3 @@ func TestMain(m *testing.M) {
return m.Run()
}())
}
func ensureFeatureEnabled(t *testing.T, instance *integration.Instance) {
ctx := instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
f, err := instance.Client.FeatureV2.GetInstanceFeatures(ctx, &feature.GetInstanceFeaturesRequest{
Inheritance: true,
})
require.NoError(t, err)
if f.Actions.GetEnabled() {
return
}
_, err = instance.Client.FeatureV2.SetInstanceFeatures(ctx, &feature.SetInstanceFeaturesRequest{
Actions: gu.Ptr(true),
})
require.NoError(t, err)
retryDuration, tick := integration.WaitForAndTickWithMaxDuration(ctx, 5*time.Minute)
require.EventuallyWithT(t,
func(ttt *assert.CollectT) {
f, err := instance.Client.FeatureV2.GetInstanceFeatures(ctx, &feature.GetInstanceFeaturesRequest{
Inheritance: true,
})
assert.NoError(ttt, err)
assert.True(ttt, f.Actions.GetEnabled())
},
retryDuration,
tick,
"timed out waiting for ensuring instance feature")
retryDuration, tick = integration.WaitForAndTickWithMaxDuration(ctx, 5*time.Minute)
require.EventuallyWithT(t,
func(ttt *assert.CollectT) {
_, err := instance.Client.ActionV2beta.ListExecutionMethods(ctx, &action.ListExecutionMethodsRequest{})
assert.NoError(ttt, err)
},
retryDuration,
tick,
"timed out waiting for ensuring instance feature call")
}

View File

@@ -19,7 +19,6 @@ import (
func TestServer_CreateTarget(t *testing.T) {
instance := integration.NewInstance(CTX)
ensureFeatureEnabled(t, instance)
isolatedIAMOwnerCTX := instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
type want struct {
id bool
@@ -244,7 +243,6 @@ func assertCreateTargetResponse(t *testing.T, creationDate, changeDate time.Time
func TestServer_UpdateTarget(t *testing.T) {
instance := integration.NewInstance(CTX)
ensureFeatureEnabled(t, instance)
isolatedIAMOwnerCTX := instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
type args struct {
ctx context.Context
@@ -463,7 +461,6 @@ func assertUpdateTargetResponse(t *testing.T, creationDate, changeDate time.Time
func TestServer_DeleteTarget(t *testing.T) {
instance := integration.NewInstance(CTX)
ensureFeatureEnabled(t, instance)
iamOwnerCtx := instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
tests := []struct {
name string

View File

@@ -23,10 +23,6 @@ const (
)
func (s *Server) GetTarget(ctx context.Context, req *action.GetTargetRequest) (*action.GetTargetResponse, error) {
if err := checkActionsEnabled(ctx); err != nil {
return nil, err
}
resp, err := s.query.GetTargetByID(ctx, req.GetId())
if err != nil {
return nil, err
@@ -46,9 +42,6 @@ type Context interface {
}
func (s *Server) ListTargets(ctx context.Context, req *action.ListTargetsRequest) (*action.ListTargetsResponse, error) {
if err := checkActionsEnabled(ctx); err != nil {
return nil, err
}
queries, err := s.ListTargetsRequestToModel(req)
if err != nil {
return nil, err
@@ -64,9 +57,6 @@ func (s *Server) ListTargets(ctx context.Context, req *action.ListTargetsRequest
}
func (s *Server) ListExecutions(ctx context.Context, req *action.ListExecutionsRequest) (*action.ListExecutionsResponse, error) {
if err := checkActionsEnabled(ctx); err != nil {
return nil, err
}
queries, err := s.ListExecutionsRequestToModel(req)
if err != nil {
return nil, err
@@ -252,12 +242,6 @@ func executionQueryToQuery(searchQuery *action.ExecutionSearchFilter) (query.Sea
return inConditionsQueryToQuery(q.InConditionsFilter)
case *action.ExecutionSearchFilter_ExecutionTypeFilter:
return executionTypeToQuery(q.ExecutionTypeFilter)
case *action.ExecutionSearchFilter_IncludeFilter:
include, err := conditionToInclude(q.IncludeFilter.GetInclude())
if err != nil {
return nil, err
}
return query.NewIncludeSearchQuery(include)
case *action.ExecutionSearchFilter_TargetFilter:
return query.NewTargetSearchQuery(q.TargetFilter.GetTargetId())
default:
@@ -333,14 +317,12 @@ func executionsToPb(executions []*query.Execution) []*action.Execution {
}
func executionToPb(e *query.Execution) *action.Execution {
targets := make([]*action.ExecutionTargetType, len(e.Targets))
targets := make([]string, len(e.Targets))
for i := range e.Targets {
switch e.Targets[i].Type {
case domain.ExecutionTargetTypeInclude:
targets[i] = &action.ExecutionTargetType{Type: &action.ExecutionTargetType_Include{Include: executionIDToCondition(e.Targets[i].Target)}}
case domain.ExecutionTargetTypeTarget:
targets[i] = &action.ExecutionTargetType{Type: &action.ExecutionTargetType_Target{Target: e.Targets[i].Target}}
case domain.ExecutionTargetTypeUnspecified:
targets[i] = e.Targets[i].Target
case domain.ExecutionTargetTypeInclude, domain.ExecutionTargetTypeUnspecified:
continue
default:
continue

View File

@@ -1,8 +1,6 @@
package action
import (
"context"
"google.golang.org/grpc"
"github.com/zitadel/zitadel/internal/api/authz"
@@ -10,7 +8,6 @@ import (
"github.com/zitadel/zitadel/internal/command"
"github.com/zitadel/zitadel/internal/config/systemdefaults"
"github.com/zitadel/zitadel/internal/query"
"github.com/zitadel/zitadel/internal/zerrors"
action "github.com/zitadel/zitadel/pkg/grpc/action/v2beta"
)
@@ -65,10 +62,3 @@ func (s *Server) AuthMethods() authz.MethodMapping {
func (s *Server) RegisterGateway() server.RegisterGatewayFunc {
return action.RegisterActionServiceHandler
}
func checkActionsEnabled(ctx context.Context) error {
if authz.GetInstance(ctx).Features().Actions {
return nil
}
return zerrors.ThrowPreconditionFailed(nil, "ACTION-8o6pvqfjhs", "Errors.Action.NotEnabled")
}

View File

@@ -14,9 +14,6 @@ import (
)
func (s *Server) CreateTarget(ctx context.Context, req *action.CreateTargetRequest) (*action.CreateTargetResponse, error) {
if err := checkActionsEnabled(ctx); err != nil {
return nil, err
}
add := createTargetToCommand(req)
instanceID := authz.GetInstance(ctx).InstanceID()
createdAt, err := s.command.AddTarget(ctx, add, instanceID)
@@ -35,9 +32,6 @@ func (s *Server) CreateTarget(ctx context.Context, req *action.CreateTargetReque
}
func (s *Server) UpdateTarget(ctx context.Context, req *action.UpdateTargetRequest) (*action.UpdateTargetResponse, error) {
if err := checkActionsEnabled(ctx); err != nil {
return nil, err
}
instanceID := authz.GetInstance(ctx).InstanceID()
update := updateTargetToCommand(req)
changedAt, err := s.command.ChangeTarget(ctx, update, instanceID)
@@ -55,9 +49,6 @@ func (s *Server) UpdateTarget(ctx context.Context, req *action.UpdateTargetReque
}
func (s *Server) DeleteTarget(ctx context.Context, req *action.DeleteTargetRequest) (*action.DeleteTargetResponse, error) {
if err := checkActionsEnabled(ctx); err != nil {
return nil, err
}
instanceID := authz.GetInstance(ctx).InstanceID()
deletedAt, err := s.command.DeleteTarget(ctx, req.GetId(), instanceID)
if err != nil {

View File

@@ -22,7 +22,6 @@ func systemFeaturesToCommand(req *feature_pb.SetSystemFeaturesRequest) (*command
TriggerIntrospectionProjections: req.OidcTriggerIntrospectionProjections,
LegacyIntrospection: req.OidcLegacyIntrospection,
UserSchema: req.UserSchema,
Actions: req.Actions,
TokenExchange: req.OidcTokenExchange,
ImprovedPerformance: improvedPerformanceListToDomain(req.ImprovedPerformance),
OIDCSingleV1SessionTermination: req.OidcSingleV1SessionTermination,
@@ -41,7 +40,6 @@ func systemFeaturesToPb(f *query.SystemFeatures) *feature_pb.GetSystemFeaturesRe
OidcLegacyIntrospection: featureSourceToFlagPb(&f.LegacyIntrospection),
UserSchema: featureSourceToFlagPb(&f.UserSchema),
OidcTokenExchange: featureSourceToFlagPb(&f.TokenExchange),
Actions: featureSourceToFlagPb(&f.Actions),
ImprovedPerformance: featureSourceToImprovedPerformanceFlagPb(&f.ImprovedPerformance),
OidcSingleV1SessionTermination: featureSourceToFlagPb(&f.OIDCSingleV1SessionTermination),
DisableUserTokenEvent: featureSourceToFlagPb(&f.DisableUserTokenEvent),
@@ -62,7 +60,6 @@ func instanceFeaturesToCommand(req *feature_pb.SetInstanceFeaturesRequest) (*com
LegacyIntrospection: req.OidcLegacyIntrospection,
UserSchema: req.UserSchema,
TokenExchange: req.OidcTokenExchange,
Actions: req.Actions,
ImprovedPerformance: improvedPerformanceListToDomain(req.ImprovedPerformance),
WebKey: req.WebKey,
DebugOIDCParentError: req.DebugOidcParentError,
@@ -83,7 +80,6 @@ func instanceFeaturesToPb(f *query.InstanceFeatures) *feature_pb.GetInstanceFeat
OidcLegacyIntrospection: featureSourceToFlagPb(&f.LegacyIntrospection),
UserSchema: featureSourceToFlagPb(&f.UserSchema),
OidcTokenExchange: featureSourceToFlagPb(&f.TokenExchange),
Actions: featureSourceToFlagPb(&f.Actions),
ImprovedPerformance: featureSourceToImprovedPerformanceFlagPb(&f.ImprovedPerformance),
WebKey: featureSourceToFlagPb(&f.WebKey),
DebugOidcParentError: featureSourceToFlagPb(&f.DebugOIDCParentError),

View File

@@ -23,7 +23,6 @@ func Test_systemFeaturesToCommand(t *testing.T) {
OidcTriggerIntrospectionProjections: gu.Ptr(false),
OidcLegacyIntrospection: nil,
UserSchema: gu.Ptr(true),
Actions: gu.Ptr(true),
OidcTokenExchange: gu.Ptr(true),
ImprovedPerformance: nil,
OidcSingleV1SessionTermination: gu.Ptr(true),
@@ -37,7 +36,6 @@ func Test_systemFeaturesToCommand(t *testing.T) {
TriggerIntrospectionProjections: gu.Ptr(false),
LegacyIntrospection: nil,
UserSchema: gu.Ptr(true),
Actions: gu.Ptr(true),
TokenExchange: gu.Ptr(true),
ImprovedPerformance: nil,
OIDCSingleV1SessionTermination: gu.Ptr(true),
@@ -74,10 +72,6 @@ func Test_systemFeaturesToPb(t *testing.T) {
Level: feature.LevelSystem,
Value: true,
},
Actions: query.FeatureSource[bool]{
Level: feature.LevelSystem,
Value: true,
},
TokenExchange: query.FeatureSource[bool]{
Level: feature.LevelSystem,
Value: false,
@@ -132,10 +126,6 @@ func Test_systemFeaturesToPb(t *testing.T) {
Enabled: false,
Source: feature_pb.Source_SOURCE_SYSTEM,
},
Actions: &feature_pb.FeatureFlag{
Enabled: true,
Source: feature_pb.Source_SOURCE_SYSTEM,
},
ImprovedPerformance: &feature_pb.ImprovedPerformanceFeatureFlag{
ExecutionPaths: []feature_pb.ImprovedPerformance{feature_pb.ImprovedPerformance_IMPROVED_PERFORMANCE_ORG_BY_ID},
Source: feature_pb.Source_SOURCE_SYSTEM,
@@ -173,7 +163,6 @@ func Test_instanceFeaturesToCommand(t *testing.T) {
OidcLegacyIntrospection: nil,
UserSchema: gu.Ptr(true),
OidcTokenExchange: gu.Ptr(true),
Actions: gu.Ptr(true),
ImprovedPerformance: nil,
WebKey: gu.Ptr(true),
DebugOidcParentError: gu.Ptr(true),
@@ -191,7 +180,6 @@ func Test_instanceFeaturesToCommand(t *testing.T) {
LegacyIntrospection: nil,
UserSchema: gu.Ptr(true),
TokenExchange: gu.Ptr(true),
Actions: gu.Ptr(true),
ImprovedPerformance: nil,
WebKey: gu.Ptr(true),
DebugOIDCParentError: gu.Ptr(true),
@@ -231,10 +219,6 @@ func Test_instanceFeaturesToPb(t *testing.T) {
Level: feature.LevelInstance,
Value: true,
},
Actions: query.FeatureSource[bool]{
Level: feature.LevelInstance,
Value: true,
},
TokenExchange: query.FeatureSource[bool]{
Level: feature.LevelSystem,
Value: false,
@@ -293,10 +277,6 @@ func Test_instanceFeaturesToPb(t *testing.T) {
Enabled: true,
Source: feature_pb.Source_SOURCE_INSTANCE,
},
Actions: &feature_pb.FeatureFlag{
Enabled: true,
Source: feature_pb.Source_SOURCE_INSTANCE,
},
OidcTokenExchange: &feature_pb.FeatureFlag{
Enabled: false,
Source: feature_pb.Source_SOURCE_SYSTEM,

View File

@@ -211,7 +211,6 @@ func TestServer_GetSystemFeatures(t *testing.T) {
assertFeatureFlag(t, tt.want.OidcTriggerIntrospectionProjections, got.OidcTriggerIntrospectionProjections)
assertFeatureFlag(t, tt.want.OidcLegacyIntrospection, got.OidcLegacyIntrospection)
assertFeatureFlag(t, tt.want.UserSchema, got.UserSchema)
assertFeatureFlag(t, tt.want.Actions, got.Actions)
})
}
}
@@ -374,10 +373,6 @@ func TestServer_GetInstanceFeatures(t *testing.T) {
Enabled: false,
Source: feature.Source_SOURCE_UNSPECIFIED,
},
Actions: &feature.FeatureFlag{
Enabled: false,
Source: feature.Source_SOURCE_UNSPECIFIED,
},
},
},
{
@@ -387,7 +382,6 @@ func TestServer_GetInstanceFeatures(t *testing.T) {
LoginDefaultOrg: gu.Ptr(true),
OidcTriggerIntrospectionProjections: gu.Ptr(false),
UserSchema: gu.Ptr(true),
Actions: gu.Ptr(true),
})
require.NoError(t, err)
},
@@ -408,10 +402,6 @@ func TestServer_GetInstanceFeatures(t *testing.T) {
Enabled: true,
Source: feature.Source_SOURCE_INSTANCE,
},
Actions: &feature.FeatureFlag{
Enabled: true,
Source: feature.Source_SOURCE_INSTANCE,
},
},
},
{
@@ -445,10 +435,6 @@ func TestServer_GetInstanceFeatures(t *testing.T) {
Enabled: false,
Source: feature.Source_SOURCE_UNSPECIFIED,
},
Actions: &feature.FeatureFlag{
Enabled: false,
Source: feature.Source_SOURCE_UNSPECIFIED,
},
},
},
}

View File

@@ -14,7 +14,6 @@ func systemFeaturesToCommand(req *feature_pb.SetSystemFeaturesRequest) *command.
TriggerIntrospectionProjections: req.OidcTriggerIntrospectionProjections,
LegacyIntrospection: req.OidcLegacyIntrospection,
UserSchema: req.UserSchema,
Actions: req.Actions,
TokenExchange: req.OidcTokenExchange,
ImprovedPerformance: improvedPerformanceListToDomain(req.ImprovedPerformance),
OIDCSingleV1SessionTermination: req.OidcSingleV1SessionTermination,
@@ -29,7 +28,6 @@ func systemFeaturesToPb(f *query.SystemFeatures) *feature_pb.GetSystemFeaturesRe
OidcLegacyIntrospection: featureSourceToFlagPb(&f.LegacyIntrospection),
UserSchema: featureSourceToFlagPb(&f.UserSchema),
OidcTokenExchange: featureSourceToFlagPb(&f.TokenExchange),
Actions: featureSourceToFlagPb(&f.Actions),
ImprovedPerformance: featureSourceToImprovedPerformanceFlagPb(&f.ImprovedPerformance),
OidcSingleV1SessionTermination: featureSourceToFlagPb(&f.OIDCSingleV1SessionTermination),
}
@@ -42,7 +40,6 @@ func instanceFeaturesToCommand(req *feature_pb.SetInstanceFeaturesRequest) *comm
LegacyIntrospection: req.OidcLegacyIntrospection,
UserSchema: req.UserSchema,
TokenExchange: req.OidcTokenExchange,
Actions: req.Actions,
ImprovedPerformance: improvedPerformanceListToDomain(req.ImprovedPerformance),
WebKey: req.WebKey,
DebugOIDCParentError: req.DebugOidcParentError,
@@ -58,7 +55,6 @@ func instanceFeaturesToPb(f *query.InstanceFeatures) *feature_pb.GetInstanceFeat
OidcLegacyIntrospection: featureSourceToFlagPb(&f.LegacyIntrospection),
UserSchema: featureSourceToFlagPb(&f.UserSchema),
OidcTokenExchange: featureSourceToFlagPb(&f.TokenExchange),
Actions: featureSourceToFlagPb(&f.Actions),
ImprovedPerformance: featureSourceToImprovedPerformanceFlagPb(&f.ImprovedPerformance),
WebKey: featureSourceToFlagPb(&f.WebKey),
DebugOidcParentError: featureSourceToFlagPb(&f.DebugOIDCParentError),

View File

@@ -22,7 +22,6 @@ func Test_systemFeaturesToCommand(t *testing.T) {
OidcTriggerIntrospectionProjections: gu.Ptr(false),
OidcLegacyIntrospection: nil,
UserSchema: gu.Ptr(true),
Actions: gu.Ptr(true),
OidcTokenExchange: gu.Ptr(true),
ImprovedPerformance: nil,
OidcSingleV1SessionTermination: gu.Ptr(true),
@@ -32,7 +31,6 @@ func Test_systemFeaturesToCommand(t *testing.T) {
TriggerIntrospectionProjections: gu.Ptr(false),
LegacyIntrospection: nil,
UserSchema: gu.Ptr(true),
Actions: gu.Ptr(true),
TokenExchange: gu.Ptr(true),
ImprovedPerformance: nil,
OIDCSingleV1SessionTermination: gu.Ptr(true),
@@ -64,10 +62,6 @@ func Test_systemFeaturesToPb(t *testing.T) {
Level: feature.LevelSystem,
Value: true,
},
Actions: query.FeatureSource[bool]{
Level: feature.LevelSystem,
Value: true,
},
TokenExchange: query.FeatureSource[bool]{
Level: feature.LevelSystem,
Value: false,
@@ -107,10 +101,6 @@ func Test_systemFeaturesToPb(t *testing.T) {
Enabled: false,
Source: feature_pb.Source_SOURCE_SYSTEM,
},
Actions: &feature_pb.FeatureFlag{
Enabled: true,
Source: feature_pb.Source_SOURCE_SYSTEM,
},
ImprovedPerformance: &feature_pb.ImprovedPerformanceFeatureFlag{
ExecutionPaths: []feature_pb.ImprovedPerformance{feature_pb.ImprovedPerformance_IMPROVED_PERFORMANCE_ORG_BY_ID},
Source: feature_pb.Source_SOURCE_SYSTEM,
@@ -131,7 +121,6 @@ func Test_instanceFeaturesToCommand(t *testing.T) {
OidcLegacyIntrospection: nil,
UserSchema: gu.Ptr(true),
OidcTokenExchange: gu.Ptr(true),
Actions: gu.Ptr(true),
ImprovedPerformance: nil,
WebKey: gu.Ptr(true),
OidcSingleV1SessionTermination: gu.Ptr(true),
@@ -142,7 +131,6 @@ func Test_instanceFeaturesToCommand(t *testing.T) {
LegacyIntrospection: nil,
UserSchema: gu.Ptr(true),
TokenExchange: gu.Ptr(true),
Actions: gu.Ptr(true),
ImprovedPerformance: nil,
WebKey: gu.Ptr(true),
OIDCSingleV1SessionTermination: gu.Ptr(true),
@@ -174,10 +162,6 @@ func Test_instanceFeaturesToPb(t *testing.T) {
Level: feature.LevelInstance,
Value: true,
},
Actions: query.FeatureSource[bool]{
Level: feature.LevelInstance,
Value: true,
},
TokenExchange: query.FeatureSource[bool]{
Level: feature.LevelSystem,
Value: false,
@@ -217,10 +201,6 @@ func Test_instanceFeaturesToPb(t *testing.T) {
Enabled: true,
Source: feature_pb.Source_SOURCE_INSTANCE,
},
Actions: &feature_pb.FeatureFlag{
Enabled: true,
Source: feature_pb.Source_SOURCE_INSTANCE,
},
OidcTokenExchange: &feature_pb.FeatureFlag{
Enabled: false,
Source: feature_pb.Source_SOURCE_SYSTEM,

View File

@@ -202,10 +202,6 @@ func TestServer_GetInstanceFeatures(t *testing.T) {
Enabled: false,
Source: feature.Source_SOURCE_UNSPECIFIED,
},
Actions: &feature.FeatureFlag{
Enabled: false,
Source: feature.Source_SOURCE_UNSPECIFIED,
},
},
},
{
@@ -215,7 +211,6 @@ func TestServer_GetInstanceFeatures(t *testing.T) {
LoginDefaultOrg: gu.Ptr(true),
OidcTriggerIntrospectionProjections: gu.Ptr(false),
UserSchema: gu.Ptr(true),
Actions: gu.Ptr(true),
})
require.NoError(t, err)
},
@@ -236,10 +231,6 @@ func TestServer_GetInstanceFeatures(t *testing.T) {
Enabled: true,
Source: feature.Source_SOURCE_INSTANCE,
},
Actions: &feature.FeatureFlag{
Enabled: true,
Source: feature.Source_SOURCE_INSTANCE,
},
},
},
{
@@ -273,10 +264,6 @@ func TestServer_GetInstanceFeatures(t *testing.T) {
Enabled: false,
Source: feature.Source_SOURCE_UNSPECIFIED,
},
Actions: &feature.FeatureFlag{
Enabled: false,
Source: feature.Source_SOURCE_UNSPECIFIED,
},
},
},
}

View File

@@ -5,12 +5,13 @@ import (
"encoding/json"
"google.golang.org/grpc"
"google.golang.org/protobuf/encoding/protojson"
"google.golang.org/protobuf/proto"
"github.com/zitadel/zitadel/internal/api/authz"
"github.com/zitadel/zitadel/internal/execution"
"github.com/zitadel/zitadel/internal/query"
"github.com/zitadel/zitadel/internal/telemetry/tracing"
"github.com/zitadel/zitadel/internal/zerrors"
)
func ExecutionHandler(queries *query.Queries) grpc.UnaryServerInterceptor {
@@ -48,7 +49,7 @@ func executeTargetsForRequest(ctx context.Context, targets []execution.Target, f
ProjectID: ctxData.ProjectID,
OrgID: ctxData.OrgID,
UserID: ctxData.UserID,
Request: req,
Request: Message{req.(proto.Message)},
}
return execution.CallTargets(ctx, targets, info)
@@ -70,8 +71,8 @@ func executeTargetsForResponse(ctx context.Context, targets []execution.Target,
ProjectID: ctxData.ProjectID,
OrgID: ctxData.OrgID,
UserID: ctxData.UserID,
Request: req,
Response: resp,
Request: Message{req.(proto.Message)},
Response: Message{resp.(proto.Message)},
}
return execution.CallTargets(ctx, targets, info)
@@ -80,12 +81,28 @@ func executeTargetsForResponse(ctx context.Context, targets []execution.Target,
var _ execution.ContextInfo = &ContextInfoRequest{}
type ContextInfoRequest struct {
FullMethod string `json:"fullMethod,omitempty"`
InstanceID string `json:"instanceID,omitempty"`
OrgID string `json:"orgID,omitempty"`
ProjectID string `json:"projectID,omitempty"`
UserID string `json:"userID,omitempty"`
Request interface{} `json:"request,omitempty"`
FullMethod string `json:"fullMethod,omitempty"`
InstanceID string `json:"instanceID,omitempty"`
OrgID string `json:"orgID,omitempty"`
ProjectID string `json:"projectID,omitempty"`
UserID string `json:"userID,omitempty"`
Request Message `json:"request,omitempty"`
}
type Message struct {
proto.Message
}
func (r *Message) MarshalJSON() ([]byte, error) {
data, err := protojson.Marshal(r.Message)
if err != nil {
return nil, err
}
return data, nil
}
func (r *Message) UnmarshalJSON(data []byte) error {
return protojson.Unmarshal(data, r.Message)
}
func (c *ContextInfoRequest) GetHTTPRequestBody() []byte {
@@ -97,26 +114,23 @@ func (c *ContextInfoRequest) GetHTTPRequestBody() []byte {
}
func (c *ContextInfoRequest) SetHTTPResponseBody(resp []byte) error {
if !json.Valid(resp) {
return zerrors.ThrowPreconditionFailed(nil, "ACTION-4m9s2", "Errors.Execution.ResponseIsNotValidJSON")
}
return json.Unmarshal(resp, c.Request)
return json.Unmarshal(resp, &c.Request)
}
func (c *ContextInfoRequest) GetContent() interface{} {
return c.Request
return c.Request.Message
}
var _ execution.ContextInfo = &ContextInfoResponse{}
type ContextInfoResponse struct {
FullMethod string `json:"fullMethod,omitempty"`
InstanceID string `json:"instanceID,omitempty"`
OrgID string `json:"orgID,omitempty"`
ProjectID string `json:"projectID,omitempty"`
UserID string `json:"userID,omitempty"`
Request interface{} `json:"request,omitempty"`
Response interface{} `json:"response,omitempty"`
FullMethod string `json:"fullMethod,omitempty"`
InstanceID string `json:"instanceID,omitempty"`
OrgID string `json:"orgID,omitempty"`
ProjectID string `json:"projectID,omitempty"`
UserID string `json:"userID,omitempty"`
Request Message `json:"request,omitempty"`
Response Message `json:"response,omitempty"`
}
func (c *ContextInfoResponse) GetHTTPRequestBody() []byte {
@@ -128,9 +142,9 @@ func (c *ContextInfoResponse) GetHTTPRequestBody() []byte {
}
func (c *ContextInfoResponse) SetHTTPResponseBody(resp []byte) error {
return json.Unmarshal(resp, c.Response)
return json.Unmarshal(resp, &c.Response)
}
func (c *ContextInfoResponse) GetContent() interface{} {
return c.Response
return c.Response.Message
}

View File

@@ -11,6 +11,9 @@ import (
"time"
"github.com/stretchr/testify/assert"
"google.golang.org/protobuf/encoding/protojson"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/types/known/structpb"
"github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/execution"
@@ -54,28 +57,28 @@ func (e *mockExecutionTarget) GetSigningKey() string {
return e.SigningKey
}
type mockContentRequest struct {
Content string
}
func newMockContentRequest(content string) *mockContentRequest {
return &mockContentRequest{
Content: content,
func newMockContentRequest(content string) proto.Message {
return &structpb.Struct{
Fields: map[string]*structpb.Value{
"content": {
Kind: &structpb.Value_StringValue{StringValue: content},
},
},
}
}
func newMockContextInfoRequest(fullMethod, request string) *ContextInfoRequest {
return &ContextInfoRequest{
FullMethod: fullMethod,
Request: newMockContentRequest(request),
Request: Message{Message: newMockContentRequest(request)},
}
}
func newMockContextInfoResponse(fullMethod, request, response string) *ContextInfoResponse {
return &ContextInfoResponse{
FullMethod: fullMethod,
Request: newMockContentRequest(request),
Response: newMockContentRequest(response),
Request: Message{Message: newMockContentRequest(request)},
Response: Message{Message: newMockContentRequest(response)},
}
}
@@ -591,7 +594,7 @@ func Test_executeTargetsForGRPCFullMethod_request(t *testing.T) {
} else {
assert.NoError(t, err)
}
assert.Equal(t, tt.res.want, resp)
assert.EqualExportedValues(t, tt.res.want, resp)
for _, closeF := range closeFuncs {
closeF()
@@ -632,7 +635,7 @@ func testServerCall(
time.Sleep(sleep)
w.Header().Set("Content-Type", "application/json")
resp, err := json.Marshal(respBody)
resp, err := protojson.Marshal(respBody.(proto.Message))
if err != nil {
http.Error(w, "error", http.StatusInternalServerError)
return
@@ -723,7 +726,8 @@ func Test_executeTargetsForGRPCFullMethod_response(t *testing.T) {
statusCode: http.StatusOK,
},
},
req: []byte{},
req: newMockContentRequest(""),
resp: newMockContentRequest(""),
},
res{
wantErr: true,
@@ -790,7 +794,7 @@ func Test_executeTargetsForGRPCFullMethod_response(t *testing.T) {
} else {
assert.NoError(t, err)
}
assert.Equal(t, tt.res.want, resp)
assert.EqualExportedValues(t, tt.res.want, resp)
for _, closeF := range closeFuncs {
closeF()