From 4d6497f6c1ddcc7de7d3521e00103850b3dc8139 Mon Sep 17 00:00:00 2001 From: adlerhurst Date: Wed, 18 Nov 2020 21:22:15 +0100 Subject: [PATCH] try with writemodel --- internal/v2/business/iam/converter.go | 4 ++ internal/v2/business/iam/member.go | 8 +-- internal/v2/business/iam/repository.go | 51 +++++++++++++++++++ internal/v2/repository/iam/aggregate.go | 19 +++++-- internal/v2/repository/iam/member.go | 37 +++++++++----- internal/v2/repository/idp/event_added.go | 30 +++++++---- internal/v2/repository/idp/event_changed.go | 44 +++++++++++----- .../v2/repository/idp/oidc/added_event.go | 14 ++++- internal/v2/repository/member/aggregate.go | 8 +-- .../v2/repository/member/event_changed.go | 37 +++++++++++--- internal/v2/repository/member/read_model.go | 8 +++ internal/v2/repository/org/member.go | 39 ++++++++++---- 12 files changed, 232 insertions(+), 67 deletions(-) diff --git a/internal/v2/business/iam/converter.go b/internal/v2/business/iam/converter.go index 1cbc36924d..8d6a27bfb0 100644 --- a/internal/v2/business/iam/converter.go +++ b/internal/v2/business/iam/converter.go @@ -115,3 +115,7 @@ func readModelToMember(readModel *member.ReadModel) *model.IAMMember { UserID: readModel.UserID, } } + +func writeModelFromMember(member *model.IAMMember) *member.WriteModel { + return &member.WriteModel{} +} diff --git a/internal/v2/business/iam/member.go b/internal/v2/business/iam/member.go index b80aabc388..b51fb2b4b0 100644 --- a/internal/v2/business/iam/member.go +++ b/internal/v2/business/iam/member.go @@ -54,13 +54,13 @@ func (r *Repository) ChangeIAMMember(ctx context.Context, member *iam_model.IAMM return nil, err } - _, currentMember := iam.Members.MemberByUserID(member.UserID) - if currentMember == nil { - return nil, caos_errs.ThrowPreconditionFailed(nil, "IAM-GPhuz", "Errors.IAM.MemberNotExisting") + existingMember, err := r.memberWriteModelByID(ctx, member.AggregateID, member.UserID) + if err != nil { + return nil, err } iamAgg := iam_repo.AggregateFromReadModel(iam). - PushEvents(iam_repo.NewMemberChangedEvent(ctx, member.UserID, member.Roles...)) + PushMemberChanged(ctx, existingMember, nil) events, err := r.eventstore.PushAggregates(ctx, iamAgg) if err != nil { diff --git a/internal/v2/business/iam/repository.go b/internal/v2/business/iam/repository.go index abd57e3d6f..f2f9543311 100644 --- a/internal/v2/business/iam/repository.go +++ b/internal/v2/business/iam/repository.go @@ -3,10 +3,12 @@ package iam import ( "context" + "github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/eventstore/v2" iam_model "github.com/caos/zitadel/internal/iam/model" "github.com/caos/zitadel/internal/tracing" iam_repo "github.com/caos/zitadel/internal/v2/repository/iam" + "github.com/caos/zitadel/internal/v2/repository/member" ) type Repository struct { @@ -46,3 +48,52 @@ func (r *Repository) iamByID(ctx context.Context, id string) (_ *iam_repo.ReadMo return readModel, nil } + +func (r *Repository) memberWriteModelByID(ctx context.Context, iamID, userID string) (member *iam_repo.MemberWriteModel, err error) { + ctx, span := tracing.NewSpan(ctx) + defer func() { span.EndWithError(err) }() + + query := eventstore.NewSearchQueryFactory(eventstore.ColumnsEvent, iam_repo.AggregateType).AggregateIDs(iamID) + + writeModel := new(memberWriteModel) + err = r.eventstore.FilterToReducer(ctx, query, writeModel) + if err != nil { + return nil, err + } + + if writeModel.isDeleted { + return nil, errors.ThrowNotFound(nil, "IAM-D8JxR", "Errors.NotFound") + } + + return &writeModel.MemberWriteModel, nil +} + +type memberWriteModel struct { + iam_repo.MemberWriteModel + + userID string + isDeleted bool +} + +func (wm *memberWriteModel) AppendEvents(events ...eventstore.EventReader) error { + for _, event := range events { + switch e := event.(type) { + case *member.AddedEvent: + if e.UserID == wm.userID { + wm.isDeleted = false + wm.MemberWriteModel.AppendEvents(event) + } + case *member.ChangedEvent: + if e.UserID == wm.userID { + wm.MemberWriteModel.AppendEvents(event) + } + case *member.RemovedEvent: + if e.UserID == wm.userID { + wm.isDeleted = true + wm.MemberWriteModel = iam_repo.MemberWriteModel{} + } + } + } + + return nil +} diff --git a/internal/v2/repository/iam/aggregate.go b/internal/v2/repository/iam/aggregate.go index 7f8a09f607..7913555da2 100644 --- a/internal/v2/repository/iam/aggregate.go +++ b/internal/v2/repository/iam/aggregate.go @@ -3,6 +3,7 @@ package iam import ( "context" + "github.com/caos/logging" "github.com/caos/zitadel/internal/eventstore/v2" ) @@ -29,7 +30,13 @@ func NewAggregate( ) *Aggregate { return &Aggregate{ - Aggregate: *eventstore.NewAggregate(id, AggregateType, resourceOwner, AggregateVersion, previousSequence), + Aggregate: *eventstore.NewAggregate( + id, + AggregateType, + resourceOwner, + AggregateVersion, + previousSequence, + ), } } @@ -46,8 +53,14 @@ func (a *Aggregate) PushMemberAdded(ctx context.Context, userID string, roles .. return a } -func (a *Aggregate) PushMemberChanged(ctx context.Context, userID string, roles ...string) *Aggregate { - a.Aggregate = *a.PushEvents(NewMemberChangedEvent(ctx, userID, roles...)) +func (a *Aggregate) PushMemberChanged(ctx context.Context, current, changed *MemberWriteModel) *Aggregate { + e, err := NewMemberChangedEvent(ctx, current, changed) + if err != nil { + logging.Log("IAM-KH21C").OnError(err).Warn("unable to push member changed") + return a + } + + a.Aggregate = *a.PushEvents(e) return a } diff --git a/internal/v2/repository/iam/member.go b/internal/v2/repository/iam/member.go index 455c40b337..0e4a03db33 100644 --- a/internal/v2/repository/iam/member.go +++ b/internal/v2/repository/iam/member.go @@ -13,7 +13,13 @@ var ( MemberRemovedEventType = iamEventTypePrefix + member.RemovedEventType ) -type MemberReadModel member.ReadModel +type MemberWriteModel struct { + member.WriteModel +} + +type MemberReadModel struct { + member.ReadModel +} func (rm *MemberReadModel) AppendEvents(events ...eventstore.EventReader) (err error) { for _, event := range events { @@ -60,20 +66,25 @@ func NewMemberAddedEvent( func NewMemberChangedEvent( ctx context.Context, - userID string, - roles ...string, -) *MemberChangedEvent { + current, + changed *MemberWriteModel, +) (*MemberChangedEvent, error) { + + m, err := member.NewChangedEvent( + eventstore.NewBaseEventForPush( + ctx, + MemberChangedEventType, + ), + ¤t.WriteModel, + &changed.WriteModel, + ) + if err != nil { + return nil, err + } return &MemberChangedEvent{ - ChangedEvent: *member.NewChangedEvent( - eventstore.NewBaseEventForPush( - ctx, - MemberChangedEventType, - ), - userID, - roles..., - ), - } + ChangedEvent: *m, + }, nil } func NewMemberRemovedEvent( diff --git a/internal/v2/repository/idp/event_added.go b/internal/v2/repository/idp/event_added.go index 433c672457..12b03af90e 100644 --- a/internal/v2/repository/idp/event_added.go +++ b/internal/v2/repository/idp/event_added.go @@ -2,30 +2,40 @@ package idp import "github.com/caos/zitadel/internal/eventstore/v2" -type AddedEvent struct { +type ConfigAddedEvent struct { eventstore.BaseEvent - ID string `idpConfigId` - Name string `name` + ID string `json:"idpConfigId"` + Name string `json:"name"` + Type ConfigType `json:"idpType,omitempty"` + StylingType StylingType `json:"stylingType,omitempty"` } func NewAddedEvent( base *eventstore.BaseEvent, configID string, name string, -) *AddedEvent { + configType ConfigType, + stylingType StylingType, +) *ConfigAddedEvent { - return &AddedEvent{ - BaseEvent: *base, - ID: configID, - Name: name, + return &ConfigAddedEvent{ + BaseEvent: *base, + ID: configID, + Name: name, + StylingType: stylingType, + Type: configType, } } -func (e *AddedEvent) CheckPrevious() bool { +func (e *ConfigAddedEvent) CheckPrevious() bool { return true } -func (e *AddedEvent) Data() interface{} { +func (e *ConfigAddedEvent) Data() interface{} { return e } + +type ConfigType uint32 + +type StylingType uint32 diff --git a/internal/v2/repository/idp/event_changed.go b/internal/v2/repository/idp/event_changed.go index 135ff8ba38..b807b87fe2 100644 --- a/internal/v2/repository/idp/event_changed.go +++ b/internal/v2/repository/idp/event_changed.go @@ -1,35 +1,51 @@ package idp -import "github.com/caos/zitadel/internal/eventstore/v2" +import ( + "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/v2" +) -type ChangedEdvent struct { +type ConfigChangedEvent struct { eventstore.BaseEvent `json:"-"` - current *ConfigAggregate - changed *ConfigAggregate + ID string `json:"idpConfigId"` + StylingType StylingType `json:"stylingType,omitempty"` - Name string `json:"name"` + hasChanged bool } -func ChangedEvent( +func NewConfigChangedEvent( base *eventstore.BaseEvent, current *ConfigAggregate, changed *ConfigAggregate, -) (*ChangedEdvent, error) { - //TODO: who to handle chanes? +) (*ConfigChangedEvent, error) { - return &ChangedEdvent{ + change := &ConfigChangedEvent{ BaseEvent: *base, - current: current, - changed: changed, - }, nil + } + + if current.ConfigID != changed.ConfigID { + change.ID = changed.ConfigID + change.hasChanged = true + } + + if current.StylingType != changed.StylingType { + change.StylingType = changed.StylingType + change.hasChanged = true + } + + if !change.hasChanged { + return nil, errors.ThrowPreconditionFailed(nil, "IDP-UBJbB", "Errors.NoChanges") + } + + return change, nil } -func (e *ChangedEdvent) CheckPrevious() bool { +func (e *ConfigChangedEvent) CheckPrevious() bool { return true } -func (e *ChangedEdvent) Data() interface{} { +func (e *ConfigChangedEvent) Data() interface{} { if e.current.Name != e.changed.Name { e.Name = e.changed.Name } diff --git a/internal/v2/repository/idp/oidc/added_event.go b/internal/v2/repository/idp/oidc/added_event.go index 59dd574fff..492ed1d938 100644 --- a/internal/v2/repository/idp/oidc/added_event.go +++ b/internal/v2/repository/idp/oidc/added_event.go @@ -1,3 +1,15 @@ package oidc -type AddedEvent struct{} +import "github.com/caos/zitadel/internal/crypto" + +type AddedEvent struct { + eventstore.BaseEvent + + IDPConfigID string `json:"idpConfigId"` + ClientID string `json:"clientId"` + Secret *crypto.CryptoValue `json:"clientSecret"` + Issuer string `json:"issuer"` + Scopes []string `json:"scpoes"` + IDPDisplayNameMapping int32 `json:"idpDisplayNameMapping,omitempty"` + UsernameMapping int32 `json:"usernameMapping,omitempty"` +} diff --git a/internal/v2/repository/member/aggregate.go b/internal/v2/repository/member/aggregate.go index e0f4c00fe6..465cc6fb5d 100644 --- a/internal/v2/repository/member/aggregate.go +++ b/internal/v2/repository/member/aggregate.go @@ -9,8 +9,10 @@ type Aggregate struct { Roles []string } -func NewMemberAggregate(userID string) *ReadModel { - return &ReadModel{ - ReadModel: *eventstore.NewReadModel(), +func NewAggregate(aggregate *eventstore.Aggregate, userID string, roles ...string) *Aggregate { + return &Aggregate{ + Aggregate: *aggregate, + Roles: roles, + UserID: userID, } } diff --git a/internal/v2/repository/member/event_changed.go b/internal/v2/repository/member/event_changed.go index 6c8acc6480..23c299bbe9 100644 --- a/internal/v2/repository/member/event_changed.go +++ b/internal/v2/repository/member/event_changed.go @@ -2,6 +2,8 @@ package member import ( "encoding/json" + "reflect" + "sort" "github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/eventstore/v2" @@ -15,8 +17,10 @@ const ( type ChangedEvent struct { eventstore.BaseEvent `json:"-"` - Roles []string `json:"roles"` - UserID string `json:"userId"` + Roles []string `json:"roles,omitempty"` + UserID string `json:"userId,omitempty"` + + hasChanged bool } func (e *ChangedEvent) CheckPrevious() bool { @@ -29,15 +33,32 @@ func (e *ChangedEvent) Data() interface{} { func NewChangedEvent( base *eventstore.BaseEvent, - userID string, - roles ...string, -) *ChangedEvent { + current, + changed *WriteModel, +) (*ChangedEvent, error) { - return &ChangedEvent{ + change := &ChangedEvent{ BaseEvent: *base, - Roles: roles, - UserID: userID, } + + if current.UserID != changed.UserID { + change.UserID = changed.UserID + change.hasChanged = true + } + + sort.Strings(current.Roles) + sort.Strings(changed.Roles) + if !reflect.DeepEqual(current.Roles, changed.Roles) { + change.Roles = changed.Roles + change.hasChanged = true + } + + if !change.hasChanged { + return nil, errors.ThrowPreconditionFailed(nil, "MEMBE-SeKlD", "Errors.NoChanges") + } + + return change, nil + } func ChangedEventMapper(event *repository.Event) (eventstore.EventReader, error) { diff --git a/internal/v2/repository/member/read_model.go b/internal/v2/repository/member/read_model.go index b8d7609e82..2aa61bf7c5 100644 --- a/internal/v2/repository/member/read_model.go +++ b/internal/v2/repository/member/read_model.go @@ -2,6 +2,8 @@ package member import "github.com/caos/zitadel/internal/eventstore/v2" +//ReadModel represenets the default member view. +// It's computed from events. type ReadModel struct { eventstore.ReadModel @@ -9,6 +11,7 @@ type ReadModel struct { Roles []string } +//NewMemberReadModel is the default constructor of ReadModel func NewMemberReadModel(userID string) *ReadModel { return &ReadModel{ ReadModel: *eventstore.NewReadModel(), @@ -16,6 +19,7 @@ func NewMemberReadModel(userID string) *ReadModel { } } +//Reduce extends eventstore.ReadModel func (rm *ReadModel) Reduce() error { for _, event := range rm.Events { switch e := event.(type) { @@ -29,3 +33,7 @@ func (rm *ReadModel) Reduce() error { } return rm.ReadModel.Reduce() } + +//WriteModel is used to create events +// It has no computed fields and represents the data +type WriteModel ReadModel diff --git a/internal/v2/repository/org/member.go b/internal/v2/repository/org/member.go index 88fe31e18d..beaaf603c7 100644 --- a/internal/v2/repository/org/member.go +++ b/internal/v2/repository/org/member.go @@ -18,6 +18,19 @@ var ( MemberRemovedEventType = orgEventTypePrefix + member.RemovedEventType ) +type MemberWriteModel struct { + member.WriteModel +} + +// func NewMemberAggregate(userID string) *MemberAggregate { +// return &MemberAggregate{ +// Aggregate: member.NewAggregate( +// eventstore.NewAggregate(userID, MemberAggregateType, "RO", AggregateVersion, 0), +// ), +// // Aggregate: member.NewMemberAggregate(userID), +// } +// } + type MembersReadModel struct { members.ReadModel } @@ -81,20 +94,24 @@ func NewMemberAddedEvent( func NewMemberChangedEvent( ctx context.Context, - userID string, - roles ...string, -) *MemberChangedEvent { + current, + changed *MemberWriteModel, +) (*MemberChangedEvent, error) { - return &MemberChangedEvent{ - ChangedEvent: *member.NewChangedEvent( - eventstore.NewBaseEventForPush( - ctx, - MemberChangedEventType, - ), - userID, - roles..., + event, err := member.NewChangedEvent( + eventstore.NewBaseEventForPush( + ctx, + MemberChangedEventType, ), + ¤t.WriteModel, + &changed.WriteModel, + ) + if err != nil { + return nil, err } + return &MemberChangedEvent{ + ChangedEvent: *event, + }, nil } func NewMemberRemovedEvent(