feat: actions (#2377)

* feat(actions): begin api

* feat(actions): begin api

* api and projections

* fix: handle multiple statements for a single event in projections

* export func type

* fix test

* update to new reduce interface

* flows in login

* feat: jwt idp

* feat: command side

* feat: add tests

* actions and flows

* fill idp views with jwt idps and return apis

* add jwtEndpoint to jwt idp

* begin jwt request handling

* add feature

* merge

* merge

* handle jwt idp

* cleanup

* bug fixes

* autoregister

* get token from specific header name

* fix: proto

* fixes

* i18n

* begin tests

* fix and log http proxy

* remove docker cache

* fixes

* usergrants in actions api

* tests adn cleanup

* cleanup

* fix add user grant

* set login context

* i18n

Co-authored-by: fabi <fabienne.gerschwiler@gmail.com>
This commit is contained in:
Livio Amstutz
2021-09-27 13:43:49 +02:00
committed by GitHub
parent 5c32fc9c12
commit ed80a8bb1e
73 changed files with 5197 additions and 64 deletions

View File

@@ -0,0 +1,261 @@
package action
import (
"context"
"encoding/json"
"time"
"github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore"
"github.com/caos/zitadel/internal/eventstore/repository"
)
const (
UniqueActionNameType = "action_names"
eventTypePrefix = eventstore.EventType("action.")
AddedEventType = eventTypePrefix + "added"
ChangedEventType = eventTypePrefix + "changed"
DeactivatedEventType = eventTypePrefix + "deactivated"
ReactivatedEventType = eventTypePrefix + "reactivated"
RemovedEventType = eventTypePrefix + "removed"
)
func NewAddActionNameUniqueConstraint(actionName, resourceOwner string) *eventstore.EventUniqueConstraint {
return eventstore.NewAddEventUniqueConstraint(
UniqueActionNameType,
actionName+":"+resourceOwner,
"Errors.Action.AlreadyExists")
}
func NewRemoveActionNameUniqueConstraint(actionName, resourceOwner string) *eventstore.EventUniqueConstraint {
return eventstore.NewRemoveEventUniqueConstraint(
UniqueActionNameType,
actionName+":"+resourceOwner)
}
type AddedEvent struct {
eventstore.BaseEvent `json:"-"`
Name string `json:"name"`
Script string `json:"script,omitempty"`
Timeout time.Duration `json:"timeout,omitempty"`
AllowedToFail bool `json:"allowedToFail"`
}
func (e *AddedEvent) Data() interface{} {
return e
}
func (e *AddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint {
return []*eventstore.EventUniqueConstraint{NewAddActionNameUniqueConstraint(e.Name, e.Aggregate().ResourceOwner)}
}
func NewAddedEvent(
ctx context.Context,
aggregate *eventstore.Aggregate,
name,
script string,
timeout time.Duration,
allowedToFail bool,
) *AddedEvent {
return &AddedEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
ctx,
aggregate,
AddedEventType,
),
Name: name,
Script: script,
Timeout: timeout,
AllowedToFail: allowedToFail,
}
}
func AddedEventMapper(event *repository.Event) (eventstore.EventReader, error) {
e := &AddedEvent{
BaseEvent: *eventstore.BaseEventFromRepo(event),
}
err := json.Unmarshal(event.Data, e)
if err != nil {
return nil, errors.ThrowInternal(err, "ACTION-4n8vs", "unable to unmarshal action added")
}
return e, nil
}
type ChangedEvent struct {
eventstore.BaseEvent `json:"-"`
Name *string `json:"name,omitempty"`
Script *string `json:"script,omitempty"`
Timeout *time.Duration `json:"timeout,omitempty"`
AllowedToFail *bool `json:"allowedToFail,omitempty"`
oldName string
}
func (e *ChangedEvent) Data() interface{} {
return e
}
func (e *ChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint {
if e.oldName == "" {
return nil
}
return []*eventstore.EventUniqueConstraint{
NewRemoveActionNameUniqueConstraint(e.oldName, e.Aggregate().ResourceOwner),
NewAddActionNameUniqueConstraint(*e.Name, e.Aggregate().ResourceOwner),
}
}
func NewChangedEvent(
ctx context.Context,
aggregate *eventstore.Aggregate,
changes []ActionChanges,
) (*ChangedEvent, error) {
if len(changes) == 0 {
return nil, errors.ThrowPreconditionFailed(nil, "ACTION-dg4t2", "Errors.NoChangesFound")
}
changeEvent := &ChangedEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
ctx,
aggregate,
ChangedEventType,
),
}
for _, change := range changes {
change(changeEvent)
}
return changeEvent, nil
}
type ActionChanges func(event *ChangedEvent)
func ChangeName(name, oldName string) func(event *ChangedEvent) {
return func(e *ChangedEvent) {
e.Name = &name
e.oldName = oldName
}
}
func ChangeScript(script string) func(event *ChangedEvent) {
return func(e *ChangedEvent) {
e.Script = &script
}
}
func ChangeTimeout(timeout time.Duration) func(event *ChangedEvent) {
return func(e *ChangedEvent) {
e.Timeout = &timeout
}
}
func ChangeAllowedToFail(allowedToFail bool) func(event *ChangedEvent) {
return func(e *ChangedEvent) {
e.AllowedToFail = &allowedToFail
}
}
func ChangedEventMapper(event *repository.Event) (eventstore.EventReader, error) {
e := &ChangedEvent{
BaseEvent: *eventstore.BaseEventFromRepo(event),
}
err := json.Unmarshal(event.Data, e)
if err != nil {
return nil, errors.ThrowInternal(err, "ACTION-4n8vs", "unable to unmarshal action changed")
}
return e, nil
}
type DeactivatedEvent struct {
eventstore.BaseEvent `json:"-"`
}
func (e *DeactivatedEvent) Data() interface{} {
return nil
}
func (e *DeactivatedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint {
return nil
}
func NewDeactivatedEvent(ctx context.Context, aggregate *eventstore.Aggregate) *DeactivatedEvent {
return &DeactivatedEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
ctx,
aggregate,
DeactivatedEventType,
),
}
}
func DeactivatedEventMapper(event *repository.Event) (eventstore.EventReader, error) {
return &DeactivatedEvent{
BaseEvent: *eventstore.BaseEventFromRepo(event),
}, nil
}
type ReactivatedEvent struct {
eventstore.BaseEvent `json:"-"`
}
func (e *ReactivatedEvent) Data() interface{} {
return nil
}
func (e *ReactivatedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint {
return nil
}
func NewReactivatedEvent(ctx context.Context, aggregate *eventstore.Aggregate) *ReactivatedEvent {
return &ReactivatedEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
ctx,
aggregate,
ReactivatedEventType,
),
}
}
func ReactivatedEventMapper(event *repository.Event) (eventstore.EventReader, error) {
return &ReactivatedEvent{
BaseEvent: *eventstore.BaseEventFromRepo(event),
}, nil
}
type RemovedEvent struct {
eventstore.BaseEvent `json:"-"`
name string
}
func (e *RemovedEvent) Data() interface{} {
return e
}
func (e *RemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint {
return []*eventstore.EventUniqueConstraint{NewRemoveActionNameUniqueConstraint(e.name, e.Aggregate().ResourceOwner)}
}
func NewRemovedEvent(
ctx context.Context,
aggregate *eventstore.Aggregate,
name string,
) *RemovedEvent {
return &RemovedEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
ctx,
aggregate,
RemovedEventType,
),
name: name,
}
}
func RemovedEventMapper(event *repository.Event) (eventstore.EventReader, error) {
return &RemovedEvent{
BaseEvent: *eventstore.BaseEventFromRepo(event),
}, nil
}

View File

@@ -0,0 +1,23 @@
package action
import "github.com/caos/zitadel/internal/eventstore"
const (
AggregateType = "action"
AggregateVersion = "v1"
)
type Aggregate struct {
eventstore.Aggregate
}
func NewAggregate(id, resourceOwner string) *Aggregate {
return &Aggregate{
Aggregate: eventstore.Aggregate{
Type: AggregateType,
Version: AggregateVersion,
ID: id,
ResourceOwner: resourceOwner,
},
}
}

View File

@@ -0,0 +1,11 @@
package action
import "github.com/caos/zitadel/internal/eventstore"
func RegisterEventMappers(es *eventstore.Eventstore) {
es.RegisterFilterEventMapper(AddedEventType, AddedEventMapper).
RegisterFilterEventMapper(ChangedEventType, ChangedEventMapper).
RegisterFilterEventMapper(DeactivatedEventType, DeactivatedEventMapper).
RegisterFilterEventMapper(ReactivatedEventType, ReactivatedEventMapper).
RegisterFilterEventMapper(RemovedEventType, RemovedEventMapper)
}

View File

@@ -40,6 +40,7 @@ type FeaturesSetEvent struct {
CustomTextMessage *bool `json:"customTextMessage,omitempty"`
CustomTextLogin *bool `json:"customTextLogin,omitempty"`
LockoutPolicy *bool `json:"lockoutPolicy,omitempty"`
Actions *bool `json:"actions,omitempty"`
}
func (e *FeaturesSetEvent) Data() interface{} {
@@ -188,6 +189,12 @@ func ChangeLockoutPolicy(lockoutPolicy bool) func(event *FeaturesSetEvent) {
}
}
func ChangeActions(actions bool) func(event *FeaturesSetEvent) {
return func(e *FeaturesSetEvent) {
e.Actions = &actions
}
}
func FeaturesSetEventMapper(event *repository.Event) (eventstore.EventReader, error) {
e := &FeaturesSetEvent{
BaseEvent: *eventstore.BaseEventFromRepo(event),

View File

@@ -0,0 +1,139 @@
package flow
import (
"encoding/json"
"github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore"
"github.com/caos/zitadel/internal/eventstore/repository"
)
const (
eventTypePrefix = eventstore.EventType("flow.")
triggerActionsPrefix = eventTypePrefix + "trigger_actions."
TriggerActionsSetEventType = triggerActionsPrefix + "set"
TriggerActionsCascadeRemovedEventType = triggerActionsPrefix + "cascade.removed"
FlowClearedEventType = eventTypePrefix + "cleared"
)
type TriggerActionsSetEvent struct {
eventstore.BaseEvent
FlowType domain.FlowType
TriggerType domain.TriggerType
ActionIDs []string
}
func (e *TriggerActionsSetEvent) Data() interface{} {
return e
}
func (e *TriggerActionsSetEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint {
return nil
}
func NewTriggerActionsSetEvent(
base *eventstore.BaseEvent,
flowType domain.FlowType,
triggerType domain.TriggerType,
actionIDs []string,
) *TriggerActionsSetEvent {
return &TriggerActionsSetEvent{
BaseEvent: *base,
FlowType: flowType,
TriggerType: triggerType,
ActionIDs: actionIDs,
}
}
func TriggerActionsSetEventMapper(event *repository.Event) (eventstore.EventReader, error) {
e := &TriggerActionsSetEvent{
BaseEvent: *eventstore.BaseEventFromRepo(event),
}
err := json.Unmarshal(event.Data, e)
if err != nil {
return nil, errors.ThrowInternal(err, "FLOW-4n8vs", "unable to unmarshal trigger actions")
}
return e, nil
}
type TriggerActionsCascadeRemovedEvent struct {
eventstore.BaseEvent
FlowType domain.FlowType
TriggerType domain.TriggerType
ActionID string
}
func (e *TriggerActionsCascadeRemovedEvent) Data() interface{} {
return e
}
func (e *TriggerActionsCascadeRemovedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint {
return nil
}
func NewTriggerActionsCascadeRemovedEvent(
base *eventstore.BaseEvent,
flowType domain.FlowType,
actionID string,
) *TriggerActionsCascadeRemovedEvent {
return &TriggerActionsCascadeRemovedEvent{
BaseEvent: *base,
FlowType: flowType,
ActionID: actionID,
}
}
func TriggerActionsCascadeRemovedEventMapper(event *repository.Event) (eventstore.EventReader, error) {
e := &TriggerActionsCascadeRemovedEvent{
BaseEvent: *eventstore.BaseEventFromRepo(event),
}
err := json.Unmarshal(event.Data, e)
if err != nil {
return nil, errors.ThrowInternal(err, "FLOW-4n8vs", "unable to unmarshal trigger actions")
}
return e, nil
}
type FlowClearedEvent struct {
eventstore.BaseEvent
FlowType domain.FlowType
}
func (e *FlowClearedEvent) Data() interface{} {
return e
}
func (e *FlowClearedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint {
return nil
}
func NewFlowClearedEvent(
base *eventstore.BaseEvent,
flowType domain.FlowType,
) *FlowClearedEvent {
return &FlowClearedEvent{
BaseEvent: *base,
FlowType: flowType,
}
}
func FlowClearedEventMapper(event *repository.Event) (eventstore.EventReader, error) {
e := &FlowClearedEvent{
BaseEvent: *eventstore.BaseEventFromRepo(event),
}
err := json.Unmarshal(event.Data, e)
if err != nil {
return nil, errors.ThrowInternal(err, "FLOW-BHfg2", "unable to unmarshal flow cleared")
}
return e, nil
}

View File

@@ -78,5 +78,8 @@ func RegisterEventMappers(es *eventstore.Eventstore) {
RegisterFilterEventMapper(IDPJWTConfigAddedEventType, IDPJWTConfigAddedEventMapper).
RegisterFilterEventMapper(IDPJWTConfigChangedEventType, IDPJWTConfigChangedEventMapper).
RegisterFilterEventMapper(FeaturesSetEventType, FeaturesSetEventMapper).
RegisterFilterEventMapper(FeaturesRemovedEventType, FeaturesRemovedEventMapper)
RegisterFilterEventMapper(FeaturesRemovedEventType, FeaturesRemovedEventMapper).
RegisterFilterEventMapper(TriggerActionsSetEventType, TriggerActionsSetEventMapper).
RegisterFilterEventMapper(TriggerActionsCascadeRemovedEventType, TriggerActionsCascadeRemovedEventMapper).
RegisterFilterEventMapper(FlowClearedEventType, FlowClearedEventMapper)
}

View File

@@ -0,0 +1,106 @@
package org
import (
"context"
"github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/eventstore"
"github.com/caos/zitadel/internal/eventstore/repository"
"github.com/caos/zitadel/internal/repository/flow"
)
var (
TriggerActionsSetEventType = orgEventTypePrefix + flow.TriggerActionsSetEventType
TriggerActionsCascadeRemovedEventType = orgEventTypePrefix + flow.TriggerActionsCascadeRemovedEventType
FlowClearedEventType = orgEventTypePrefix + flow.FlowClearedEventType
)
type TriggerActionsSetEvent struct {
flow.TriggerActionsSetEvent
}
func NewTriggerActionsSetEvent(
ctx context.Context,
aggregate *eventstore.Aggregate,
flowType domain.FlowType,
triggerType domain.TriggerType,
actionIDs []string,
) *TriggerActionsSetEvent {
return &TriggerActionsSetEvent{
TriggerActionsSetEvent: *flow.NewTriggerActionsSetEvent(
eventstore.NewBaseEventForPush(
ctx,
aggregate,
TriggerActionsSetEventType),
flowType,
triggerType,
actionIDs),
}
}
func TriggerActionsSetEventMapper(event *repository.Event) (eventstore.EventReader, error) {
e, err := flow.TriggerActionsSetEventMapper(event)
if err != nil {
return nil, err
}
return &TriggerActionsSetEvent{TriggerActionsSetEvent: *e.(*flow.TriggerActionsSetEvent)}, nil
}
type TriggerActionsCascadeRemovedEvent struct {
flow.TriggerActionsCascadeRemovedEvent
}
func NewTriggerActionsCascadeRemovedEvent(
ctx context.Context,
aggregate *eventstore.Aggregate,
flowType domain.FlowType,
actionID string,
) *TriggerActionsCascadeRemovedEvent {
return &TriggerActionsCascadeRemovedEvent{
TriggerActionsCascadeRemovedEvent: *flow.NewTriggerActionsCascadeRemovedEvent(
eventstore.NewBaseEventForPush(
ctx,
aggregate,
TriggerActionsCascadeRemovedEventType),
flowType,
actionID),
}
}
func TriggerActionsCascadeRemovedEventMapper(event *repository.Event) (eventstore.EventReader, error) {
e, err := flow.TriggerActionsCascadeRemovedEventMapper(event)
if err != nil {
return nil, err
}
return &TriggerActionsCascadeRemovedEvent{TriggerActionsCascadeRemovedEvent: *e.(*flow.TriggerActionsCascadeRemovedEvent)}, nil
}
type FlowClearedEvent struct {
flow.FlowClearedEvent
}
func NewFlowClearedEvent(
ctx context.Context,
aggregate *eventstore.Aggregate,
flowType domain.FlowType,
) *FlowClearedEvent {
return &FlowClearedEvent{
FlowClearedEvent: *flow.NewFlowClearedEvent(
eventstore.NewBaseEventForPush(
ctx,
aggregate,
FlowClearedEventType),
flowType),
}
}
func FlowClearedEventMapper(event *repository.Event) (eventstore.EventReader, error) {
e, err := flow.FlowClearedEventMapper(event)
if err != nil {
return nil, err
}
return &FlowClearedEvent{FlowClearedEvent: *e.(*flow.FlowClearedEvent)}, nil
}