zitadel/internal/command/action_v2_execution_model.go
Stefan Benz f37113194d
fix: actions v2 circular check for includes (#7563)
Add a check for circular includes in action v2 executions, so that no
self-includes or infinite loops can happen.

Closes #7445 

### Definition of Ready

- [x] I am happy with the code
- [x] Short description of the feature/issue is added in the pr
description
- [x] PR is linked to the corresponding user story
- [x] Acceptance criteria are met
- [x] All open todos and follow ups are defined in a new ticket and
justified
- [x] Deviations from the acceptance criteria and design are agreed with
the PO and documented.
- [x] No debug or dead code
- [x] My code has no repetitions
- [x] Critical parts are tested automatically
- [x] Where possible E2E tests are implemented
- [x] Documentation/examples are up-to-date
- [x] All non-functional requirements are met
- [x] Functionality of the acceptance criteria is checked manually on
the dev system.

---------

Co-authored-by: Livio Spring <livio.a@gmail.com>
2024-05-22 16:05:06 +00:00

135 lines
3.5 KiB
Go

package command
import (
"slices"
"github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/eventstore"
"github.com/zitadel/zitadel/internal/repository/execution"
)
type ExecutionWriteModel struct {
eventstore.WriteModel
Targets []string
Includes []string
ExecutionTargets []*execution.Target
}
func (e *ExecutionWriteModel) IncludeList() []string {
includes := make([]string, 0)
for i := range e.ExecutionTargets {
if e.ExecutionTargets[i].Type == domain.ExecutionTargetTypeInclude {
includes = append(includes, e.ExecutionTargets[i].Target)
}
}
return includes
}
func (e *ExecutionWriteModel) Exists() bool {
return len(e.ExecutionTargets) > 0 || len(e.Includes) > 0 || len(e.Targets) > 0
}
func NewExecutionWriteModel(id string, resourceOwner string) *ExecutionWriteModel {
return &ExecutionWriteModel{
WriteModel: eventstore.WriteModel{
AggregateID: id,
ResourceOwner: resourceOwner,
InstanceID: resourceOwner,
},
}
}
func (wm *ExecutionWriteModel) Reduce() error {
for _, event := range wm.Events {
switch e := event.(type) {
case *execution.SetEvent:
wm.Targets = e.Targets
wm.Includes = e.Includes
case *execution.SetEventV2:
wm.ExecutionTargets = e.Targets
case *execution.RemovedEvent:
wm.Targets = nil
wm.Includes = nil
wm.ExecutionTargets = nil
}
}
return wm.WriteModel.Reduce()
}
func (wm *ExecutionWriteModel) Query() *eventstore.SearchQueryBuilder {
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent).
ResourceOwner(wm.ResourceOwner).
AddQuery().
AggregateTypes(execution.AggregateType).
AggregateIDs(wm.AggregateID).
EventTypes(execution.SetEventType,
execution.SetEventV2Type,
execution.RemovedEventType).
Builder()
}
func ExecutionAggregateFromWriteModel(wm *eventstore.WriteModel) *eventstore.Aggregate {
return &eventstore.Aggregate{
ID: wm.AggregateID,
Type: execution.AggregateType,
ResourceOwner: wm.ResourceOwner,
InstanceID: wm.InstanceID,
Version: execution.AggregateVersion,
}
}
type ExecutionsExistWriteModel struct {
eventstore.WriteModel
ids []string
existingIDs []string
}
func (e *ExecutionsExistWriteModel) AllExists() bool {
return len(e.ids) == len(e.existingIDs)
}
func NewExecutionsExistWriteModel(ids []string, resourceOwner string) *ExecutionsExistWriteModel {
return &ExecutionsExistWriteModel{
WriteModel: eventstore.WriteModel{
ResourceOwner: resourceOwner,
InstanceID: resourceOwner,
},
ids: ids,
}
}
func (wm *ExecutionsExistWriteModel) Reduce() error {
for _, event := range wm.Events {
switch e := event.(type) {
case *execution.SetEvent:
if !slices.Contains(wm.existingIDs, e.Aggregate().ID) {
wm.existingIDs = append(wm.existingIDs, e.Aggregate().ID)
}
case *execution.SetEventV2:
if !slices.Contains(wm.existingIDs, e.Aggregate().ID) {
wm.existingIDs = append(wm.existingIDs, e.Aggregate().ID)
}
case *execution.RemovedEvent:
i := slices.Index(wm.existingIDs, e.Aggregate().ID)
if i >= 0 {
wm.existingIDs = slices.Delete(wm.existingIDs, i, i+1)
}
}
}
return wm.WriteModel.Reduce()
}
func (wm *ExecutionsExistWriteModel) Query() *eventstore.SearchQueryBuilder {
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent).
ResourceOwner(wm.ResourceOwner).
AddQuery().
AggregateTypes(execution.AggregateType).
AggregateIDs(wm.ids...).
EventTypes(execution.SetEventType,
execution.SetEventV2Type,
execution.RemovedEventType).
Builder()
}