diff --git a/internal/command/main_test.go b/internal/command/main_test.go index 4b0564c3e..f77428625 100644 --- a/internal/command/main_test.go +++ b/internal/command/main_test.go @@ -2,6 +2,7 @@ package command import ( "context" + "database/sql" "testing" "time" @@ -50,7 +51,7 @@ func eventPusherToEvents(eventsPushes ...eventstore.Command) []*repository.Event events[i] = &repository.Event{ AggregateID: event.Aggregate().ID, AggregateType: repository.AggregateType(event.Aggregate().Type), - ResourceOwner: event.Aggregate().ResourceOwner, + ResourceOwner: sql.NullString{String: event.Aggregate().ResourceOwner, Valid: event.Aggregate().ResourceOwner != ""}, EditorService: event.EditorService(), EditorUser: event.EditorUser(), Type: repository.EventType(event.Type()), @@ -152,7 +153,7 @@ func eventFromEventPusher(event eventstore.Command) *repository.Event { Version: repository.Version(event.Aggregate().Version), AggregateID: event.Aggregate().ID, AggregateType: repository.AggregateType(event.Aggregate().Type), - ResourceOwner: event.Aggregate().ResourceOwner, + ResourceOwner: sql.NullString{String: event.Aggregate().ResourceOwner, Valid: event.Aggregate().ResourceOwner != ""}, } } diff --git a/internal/command/org_member.go b/internal/command/org_member.go index 6459eee4a..9df8b0839 100644 --- a/internal/command/org_member.go +++ b/internal/command/org_member.go @@ -42,7 +42,7 @@ func (c *Commands) addOrgMember(ctx context.Context, orgAgg *eventstore.Aggregat if !member.IsValid() { return nil, caos_errs.ThrowInvalidArgument(nil, "Org-W8m4l", "Errors.Org.MemberInvalid") } - if len(domain.CheckForInvalidRoles(member.Roles, domain.OrgRolePrefix, c.zitadelRoles)) > 0 { + if len(domain.CheckForInvalidRoles(member.Roles, domain.OrgRolePrefix, c.zitadelRoles)) > 0 && len(domain.CheckForInvalidRoles(member.Roles, domain.RoleSelfManagementGlobal, c.zitadelRoles)) > 0 { return nil, caos_errs.ThrowInvalidArgument(nil, "Org-4N8es", "Errors.Org.MemberInvalid") } err := c.eventstore.FilterToQueryReducer(ctx, addedMember) diff --git a/internal/command/setup_step21.go b/internal/command/setup_step21.go index 2c772ab9d..f3d9d3e60 100644 --- a/internal/command/setup_step21.go +++ b/internal/command/setup_step21.go @@ -51,6 +51,7 @@ func newGlobalOrgMemberWriteModel(orgID, role string) *globalOrgMembersWriteMode return &globalOrgMembersWriteModel{ WriteModel: eventstore.WriteModel{ ResourceOwner: orgID, + AggregateID: orgID, }, role: role, members: make(map[string][]string), @@ -90,7 +91,7 @@ func (wm *globalOrgMembersWriteModel) Query() *eventstore.SearchQueryBuilder { return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent). AddQuery(). AggregateTypes(org.AggregateType). - AggregateIDs(wm.ResourceOwner). + AggregateIDs(wm.AggregateID). EventTypes( org.MemberAddedEventType, org.MemberChangedEventType, diff --git a/internal/eventstore/event_base.go b/internal/eventstore/event_base.go index 30f727c41..d97654888 100644 --- a/internal/eventstore/event_base.go +++ b/internal/eventstore/event_base.go @@ -78,7 +78,7 @@ func BaseEventFromRepo(event *repository.Event) *BaseEvent { aggregate: Aggregate{ ID: event.AggregateID, Type: AggregateType(event.AggregateType), - ResourceOwner: event.ResourceOwner, + ResourceOwner: event.ResourceOwner.String, Version: Version(event.Version), }, EventType: EventType(event.Type), diff --git a/internal/eventstore/eventstore.go b/internal/eventstore/eventstore.go index de47eda12..9bc195ae3 100644 --- a/internal/eventstore/eventstore.go +++ b/internal/eventstore/eventstore.go @@ -2,6 +2,7 @@ package eventstore import ( "context" + "database/sql" "encoding/json" "reflect" "sync" @@ -79,7 +80,7 @@ func commandsToRepository(cmds []Command) (events []*repository.Event, constrain events[i] = &repository.Event{ AggregateID: cmd.Aggregate().ID, AggregateType: repository.AggregateType(cmd.Aggregate().Type), - ResourceOwner: cmd.Aggregate().ResourceOwner, + ResourceOwner: sql.NullString{String: cmd.Aggregate().ResourceOwner, Valid: cmd.Aggregate().ResourceOwner != ""}, EditorService: cmd.EditorService(), EditorUser: cmd.EditorUser(), Type: repository.EventType(cmd.Type()), diff --git a/internal/eventstore/eventstore_test.go b/internal/eventstore/eventstore_test.go index c2b802677..d055989ba 100644 --- a/internal/eventstore/eventstore_test.go +++ b/internal/eventstore/eventstore_test.go @@ -2,6 +2,7 @@ package eventstore import ( "context" + "database/sql" "fmt" "reflect" "sync" @@ -376,7 +377,7 @@ func TestEventstore_aggregatesToEvents(t *testing.T) { Data: []byte(nil), EditorService: "editorService", EditorUser: "editorUser", - ResourceOwner: "caos", + ResourceOwner: sql.NullString{String: "caos", Valid: true}, Type: "test.event", Version: "v1", }, @@ -412,7 +413,7 @@ func TestEventstore_aggregatesToEvents(t *testing.T) { Data: []byte(nil), EditorService: "editorService", EditorUser: "editorUser", - ResourceOwner: "caos", + ResourceOwner: sql.NullString{String: "caos", Valid: true}, Type: "test.event", Version: "v1", }, @@ -422,7 +423,7 @@ func TestEventstore_aggregatesToEvents(t *testing.T) { Data: []byte(nil), EditorService: "editorService", EditorUser: "editorUser", - ResourceOwner: "caos", + ResourceOwner: sql.NullString{String: "caos", Valid: true}, Type: "test.event", Version: "v1", }, @@ -446,6 +447,143 @@ func TestEventstore_aggregatesToEvents(t *testing.T) { wantErr: true, }, }, + { + name: "no aggregate id", + args: args{ + events: []Command{ + &testEvent{ + BaseEvent: *NewBaseEventForPush( + service.WithService(authz.NewMockContext("resourceOwner", "editorUser"), "editorService"), + NewAggregate( + authz.NewMockContext("caos", "adlerhurst"), + "", + "test.aggregate", + "v1", + ), + "test.event", + ), + data: func() interface{} { + return nil + }, + }, + }, + }, + res: res{ + wantErr: true, + }, + }, + { + name: "no aggregate type", + args: args{ + events: []Command{ + &testEvent{ + BaseEvent: *NewBaseEventForPush( + service.WithService(authz.NewMockContext("resourceOwner", "editorUser"), "editorService"), + NewAggregate( + authz.NewMockContext("caos", "adlerhurst"), + "id", + "", + "v1", + ), + "test.event", + ), + data: func() interface{} { + return nil + }, + }, + }, + }, + res: res{ + wantErr: true, + }, + }, + { + name: "no aggregate version", + args: args{ + events: []Command{ + &testEvent{ + BaseEvent: *NewBaseEventForPush( + service.WithService(authz.NewMockContext("resourceOwner", "editorUser"), "editorService"), + NewAggregate( + authz.NewMockContext("caos", "adlerhurst"), + "id", + "test.aggregate", + "", + ), + "test.event", + ), + data: func() interface{} { + return nil + }, + }, + }, + }, + res: res{ + wantErr: true, + }, + }, + { + name: "no event type", + args: args{ + events: []Command{ + &testEvent{ + BaseEvent: *NewBaseEventForPush( + service.WithService(authz.NewMockContext("resourceOwner", "editorUser"), "editorService"), + NewAggregate( + authz.NewMockContext("caos", "adlerhurst"), + "id", + "test.aggregate", + "v1", + ), + "", + ), + data: func() interface{} { + return nil + }, + }, + }, + }, + res: res{ + wantErr: true, + }, + }, + { + name: "no resourceowner", + args: args{ + events: []Command{ + &testEvent{ + BaseEvent: *NewBaseEventForPush( + service.WithService(authz.NewMockContext("", "editorUser"), "editorService"), + NewAggregate( + authz.NewMockContext("", "adlerhurst"), + "id", + "test.aggregate", + "v1", + ), + "test.event", + ), + data: func() interface{} { + return nil + }, + }, + }, + }, + res: res{ + wantErr: false, + events: []*repository.Event{ + { + AggregateID: "id", + AggregateType: "test.aggregate", + Data: []byte(nil), + EditorService: "editorService", + EditorUser: "editorUser", + ResourceOwner: sql.NullString{String: "", Valid: false}, + Type: "test.event", + Version: "v1", + }, + }, + }, + }, { name: "multiple aggregates", args: args{ @@ -483,7 +621,7 @@ func TestEventstore_aggregatesToEvents(t *testing.T) { Data: []byte(nil), EditorService: "editorService", EditorUser: "editorUser", - ResourceOwner: "caos", + ResourceOwner: sql.NullString{String: "caos", Valid: true}, Type: "test.event", Version: "v1", }, @@ -493,7 +631,7 @@ func TestEventstore_aggregatesToEvents(t *testing.T) { Data: []byte(nil), EditorService: "editorService", EditorUser: "editorUser", - ResourceOwner: "caos", + ResourceOwner: sql.NullString{String: "caos", Valid: true}, Type: "test.event", Version: "v1", }, @@ -505,7 +643,7 @@ func TestEventstore_aggregatesToEvents(t *testing.T) { Data: []byte(nil), EditorService: "editorService", EditorUser: "editorUser", - ResourceOwner: "caos", + ResourceOwner: sql.NullString{String: "caos", Valid: true}, Type: "test.event", Version: "v1", }, @@ -622,7 +760,7 @@ func TestEventstore_Push(t *testing.T) { Data: []byte(nil), EditorService: "editorService", EditorUser: "editorUser", - ResourceOwner: "caos", + ResourceOwner: sql.NullString{String: "caos", Valid: true}, Type: "test.event", Version: "v1", }, @@ -665,7 +803,7 @@ func TestEventstore_Push(t *testing.T) { Data: []byte(nil), EditorService: "editorService", EditorUser: "editorUser", - ResourceOwner: "caos", + ResourceOwner: sql.NullString{String: "caos", Valid: true}, Type: "test.event", Version: "v1", }, @@ -675,7 +813,7 @@ func TestEventstore_Push(t *testing.T) { Data: []byte(nil), EditorService: "editorService", EditorUser: "editorUser", - ResourceOwner: "caos", + ResourceOwner: sql.NullString{String: "caos", Valid: true}, Type: "test.event", Version: "v1", }, @@ -729,7 +867,7 @@ func TestEventstore_Push(t *testing.T) { Data: []byte(nil), EditorService: "editorService", EditorUser: "editorUser", - ResourceOwner: "caos", + ResourceOwner: sql.NullString{String: "caos", Valid: true}, Type: "test.event", Version: "v1", }, @@ -739,7 +877,7 @@ func TestEventstore_Push(t *testing.T) { Data: []byte(nil), EditorService: "editorService", EditorUser: "editorUser", - ResourceOwner: "caos", + ResourceOwner: sql.NullString{String: "caos", Valid: true}, Type: "test.event", Version: "v1", }, @@ -751,7 +889,7 @@ func TestEventstore_Push(t *testing.T) { Data: []byte(nil), EditorService: "editorService", EditorUser: "editorUser", - ResourceOwner: "caos", + ResourceOwner: sql.NullString{String: "caos", Valid: true}, Type: "test.event", Version: "v1", }, @@ -1323,7 +1461,7 @@ func compareEvents(t *testing.T, want, got *repository.Event) { t.Errorf("wrong editor user got %q want %q", got.EditorUser, want.EditorUser) } if want.ResourceOwner != got.ResourceOwner { - t.Errorf("wrong resource owner got %q want %q", got.ResourceOwner, want.ResourceOwner) + t.Errorf("wrong resource owner got %q want %q", got.ResourceOwner.String, want.ResourceOwner.String) } if want.Type != got.Type { t.Errorf("wrong event type got %q want %q", got.Type, want.Type) diff --git a/internal/eventstore/repository/event.go b/internal/eventstore/repository/event.go index f176da068..428339b08 100644 --- a/internal/eventstore/repository/event.go +++ b/internal/eventstore/repository/event.go @@ -1,6 +1,7 @@ package repository import ( + "database/sql" "time" ) @@ -54,7 +55,7 @@ type Event struct { //ResourceOwner is the organisation which owns this aggregate // an aggregate can only be managed by one organisation // use the ID of the org - ResourceOwner string + ResourceOwner sql.NullString } //EventType is the description of the change diff --git a/internal/eventstore/repository/sql/crdb_test.go b/internal/eventstore/repository/sql/crdb_test.go index e45ad208f..d4ffe9e17 100644 --- a/internal/eventstore/repository/sql/crdb_test.go +++ b/internal/eventstore/repository/sql/crdb_test.go @@ -2,11 +2,13 @@ package sql import ( "context" + "database/sql" "sync" "testing" - "github.com/caos/zitadel/internal/eventstore/repository" "github.com/lib/pq" + + "github.com/caos/zitadel/internal/eventstore/repository" ) func TestCRDB_placeholder(t *testing.T) { @@ -928,8 +930,8 @@ func TestCRDB_Push_ResourceOwner(t *testing.T) { name: "two events of same aggregate same resource owner", args: args{ events: []*repository.Event{ - generateEvent(t, "500", func(e *repository.Event) { e.ResourceOwner = "caos" }), - generateEvent(t, "500", func(e *repository.Event) { e.ResourceOwner = "caos" }), + generateEvent(t, "500", func(e *repository.Event) { e.ResourceOwner = sql.NullString{String: "caos", Valid: true} }), + generateEvent(t, "500", func(e *repository.Event) { e.ResourceOwner = sql.NullString{String: "caos", Valid: true} }), }, }, fields: fields{ @@ -944,8 +946,8 @@ func TestCRDB_Push_ResourceOwner(t *testing.T) { name: "two events of different aggregate same resource owner", args: args{ events: []*repository.Event{ - generateEvent(t, "501", func(e *repository.Event) { e.ResourceOwner = "caos" }), - generateEvent(t, "502", func(e *repository.Event) { e.ResourceOwner = "caos" }), + generateEvent(t, "501", func(e *repository.Event) { e.ResourceOwner = sql.NullString{String: "caos", Valid: true} }), + generateEvent(t, "502", func(e *repository.Event) { e.ResourceOwner = sql.NullString{String: "caos", Valid: true} }), }, }, fields: fields{ @@ -960,8 +962,8 @@ func TestCRDB_Push_ResourceOwner(t *testing.T) { name: "two events of different aggregate different resource owner", args: args{ events: []*repository.Event{ - generateEvent(t, "503", func(e *repository.Event) { e.ResourceOwner = "caos" }), - generateEvent(t, "504", func(e *repository.Event) { e.ResourceOwner = "zitadel" }), + generateEvent(t, "503", func(e *repository.Event) { e.ResourceOwner = sql.NullString{String: "caos", Valid: true} }), + generateEvent(t, "504", func(e *repository.Event) { e.ResourceOwner = sql.NullString{String: "zitadel", Valid: true} }), }, }, fields: fields{ @@ -976,10 +978,10 @@ func TestCRDB_Push_ResourceOwner(t *testing.T) { name: "events of different aggregate different resource owner", args: args{ events: []*repository.Event{ - generateEvent(t, "505", func(e *repository.Event) { e.ResourceOwner = "caos" }), - generateEvent(t, "505", func(e *repository.Event) { e.ResourceOwner = "caos" }), - generateEvent(t, "506", func(e *repository.Event) { e.ResourceOwner = "zitadel" }), - generateEvent(t, "506", func(e *repository.Event) { e.ResourceOwner = "zitadel" }), + generateEvent(t, "505", func(e *repository.Event) { e.ResourceOwner = sql.NullString{String: "caos", Valid: true} }), + generateEvent(t, "505", func(e *repository.Event) { e.ResourceOwner = sql.NullString{String: "caos", Valid: true} }), + generateEvent(t, "506", func(e *repository.Event) { e.ResourceOwner = sql.NullString{String: "zitadel", Valid: true} }), + generateEvent(t, "506", func(e *repository.Event) { e.ResourceOwner = sql.NullString{String: "zitadel", Valid: true} }), }, }, fields: fields{ @@ -994,10 +996,10 @@ func TestCRDB_Push_ResourceOwner(t *testing.T) { name: "events of different aggregate different resource owner per event", args: args{ events: []*repository.Event{ - generateEvent(t, "507", func(e *repository.Event) { e.ResourceOwner = "caos" }), - generateEvent(t, "507", func(e *repository.Event) { e.ResourceOwner = "ignored" }), - generateEvent(t, "508", func(e *repository.Event) { e.ResourceOwner = "zitadel" }), - generateEvent(t, "508", func(e *repository.Event) { e.ResourceOwner = "ignored" }), + generateEvent(t, "507", func(e *repository.Event) { e.ResourceOwner = sql.NullString{String: "caos", Valid: true} }), + generateEvent(t, "507", func(e *repository.Event) { e.ResourceOwner = sql.NullString{String: "ignored", Valid: true} }), + generateEvent(t, "508", func(e *repository.Event) { e.ResourceOwner = sql.NullString{String: "zitadel", Valid: true} }), + generateEvent(t, "508", func(e *repository.Event) { e.ResourceOwner = sql.NullString{String: "ignored", Valid: true} }), }, }, fields: fields{ @@ -1012,10 +1014,10 @@ func TestCRDB_Push_ResourceOwner(t *testing.T) { name: "events of one aggregate different resource owner per event", args: args{ events: []*repository.Event{ - generateEvent(t, "509", func(e *repository.Event) { e.ResourceOwner = "caos" }), - generateEvent(t, "509", func(e *repository.Event) { e.ResourceOwner = "ignored" }), - generateEvent(t, "509", func(e *repository.Event) { e.ResourceOwner = "ignored" }), - generateEvent(t, "509", func(e *repository.Event) { e.ResourceOwner = "ignored" }), + generateEvent(t, "509", func(e *repository.Event) { e.ResourceOwner = sql.NullString{String: "caos", Valid: true} }), + generateEvent(t, "509", func(e *repository.Event) { e.ResourceOwner = sql.NullString{String: "ignored", Valid: true} }), + generateEvent(t, "509", func(e *repository.Event) { e.ResourceOwner = sql.NullString{String: "ignored", Valid: true} }), + generateEvent(t, "509", func(e *repository.Event) { e.ResourceOwner = sql.NullString{String: "ignored", Valid: true} }), }, }, fields: fields{ @@ -1042,8 +1044,8 @@ func TestCRDB_Push_ResourceOwner(t *testing.T) { } for i, event := range tt.args.events { - if event.ResourceOwner != tt.res.resourceOwners[i] { - t.Errorf("resource owner not expected want: %q got: %q", tt.res.resourceOwners[i], event.ResourceOwner) + if event.ResourceOwner.String != tt.res.resourceOwners[i] { + t.Errorf("resource owner not expected want: %q got: %q", tt.res.resourceOwners[i], event.ResourceOwner.String) } } @@ -1087,7 +1089,7 @@ func generateEvent(t *testing.T, aggregateID string, opts ...func(*repository.Ev AggregateType: repository.AggregateType(t.Name()), EditorService: "svc", EditorUser: "user", - ResourceOwner: "ro", + ResourceOwner: sql.NullString{String: "ro", Valid: true}, Type: "test.created", Version: "v1", } diff --git a/internal/eventstore/repository/sql/query_test.go b/internal/eventstore/repository/sql/query_test.go index 20e1a9688..60ed92698 100644 --- a/internal/eventstore/repository/sql/query_test.go +++ b/internal/eventstore/repository/sql/query_test.go @@ -9,9 +9,10 @@ import ( "time" "github.com/DATA-DOG/go-sqlmock" + "github.com/lib/pq" + "github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/eventstore/repository" - "github.com/lib/pq" ) func Test_getCondition(t *testing.T) { @@ -135,7 +136,7 @@ func Test_prepareColumns(t *testing.T) { }, }, fields: fields{ - dbRow: []interface{}{time.Time{}, repository.EventType(""), uint64(5), Sequence(0), Sequence(0), Data(nil), "", "", "", repository.AggregateType("user"), "hodor", repository.Version("")}, + dbRow: []interface{}{time.Time{}, repository.EventType(""), uint64(5), Sequence(0), Sequence(0), Data(nil), "", "", sql.NullString{String: ""}, repository.AggregateType("user"), "hodor", repository.Version("")}, }, }, { @@ -416,11 +417,11 @@ func Test_query_events_with_crdb(t *testing.T) { fields: fields{ client: testCRDBClient, existingEvents: []*repository.Event{ - generateEvent(t, "306", func(e *repository.Event) { e.ResourceOwner = "caos" }), - generateEvent(t, "307", func(e *repository.Event) { e.ResourceOwner = "caos" }), - generateEvent(t, "308", func(e *repository.Event) { e.ResourceOwner = "caos" }), - generateEvent(t, "309", func(e *repository.Event) { e.ResourceOwner = "orgID" }), - generateEvent(t, "309", func(e *repository.Event) { e.ResourceOwner = "orgID" }), + generateEvent(t, "306", func(e *repository.Event) { e.ResourceOwner = sql.NullString{String: "caos", Valid: true} }), + generateEvent(t, "307", func(e *repository.Event) { e.ResourceOwner = sql.NullString{String: "caos", Valid: true} }), + generateEvent(t, "308", func(e *repository.Event) { e.ResourceOwner = sql.NullString{String: "caos", Valid: true} }), + generateEvent(t, "309", func(e *repository.Event) { e.ResourceOwner = sql.NullString{String: "orgID", Valid: true} }), + generateEvent(t, "309", func(e *repository.Event) { e.ResourceOwner = sql.NullString{String: "orgID", Valid: true} }), }, }, res: res{ diff --git a/internal/query/projection/event_test.go b/internal/query/projection/event_test.go index 6dd1225ef..7ad27fd0b 100644 --- a/internal/query/projection/event_test.go +++ b/internal/query/projection/event_test.go @@ -1,6 +1,7 @@ package projection import ( + "database/sql" "testing" "time" @@ -24,7 +25,7 @@ func testEvent( Data: data, Version: "v1", AggregateID: "agg-id", - ResourceOwner: "ro-id", + ResourceOwner: sql.NullString{String: "ro-id", Valid: true}, ID: "event-id", EditorService: "editor-svc", EditorUser: "editor-user",