diff --git a/internal/api/grpc/org/v2beta/integration_test/org_test.go b/internal/api/grpc/org/v2beta/integration_test/org_test.go index 0d3b920afe..59fe235bad 100644 --- a/internal/api/grpc/org/v2beta/integration_test/org_test.go +++ b/internal/api/grpc/org/v2beta/integration_test/org_test.go @@ -47,14 +47,17 @@ func TestMain(m *testing.M) { func TestServer_CreateOrganization(t *testing.T) { idpResp := Instance.AddGenericOAuthProvider(CTX, Instance.DefaultOrg.Id) - tests := []struct { - name string - ctx context.Context - req *v2beta_org.CreateOrganizationRequest - id string - want *v2beta_org.CreateOrganizationResponse - wantErr bool - }{ + type test struct { + name string + ctx context.Context + req *v2beta_org.CreateOrganizationRequest + id string + testfunc func(ctx context.Context, t *testing.T) + want *v2beta_org.CreateOrganizationResponse + wantErr bool + } + + tests := []test{ { name: "missing permission", ctx: Instance.WithAuthorization(CTX, integration.UserTypeOrgOwner), @@ -73,6 +76,25 @@ func TestServer_CreateOrganization(t *testing.T) { }, wantErr: true, }, + func() test { + orgName := gofakeit.Name() + return test{ + name: "adding org with same name twice", + ctx: CTX, + req: &v2beta_org.CreateOrganizationRequest{ + Name: orgName, + Admins: nil, + }, + testfunc: func(ctx context.Context, t *testing.T) { + // create org initally + _, err := Client.CreateOrganization(ctx, &v2beta_org.CreateOrganizationRequest{ + Name: orgName, + }) + require.NoError(t, err) + }, + wantErr: true, + } + }(), { name: "invalid admin type", ctx: CTX, @@ -210,9 +232,33 @@ func TestServer_CreateOrganization(t *testing.T) { }, want: &v2beta_org.CreateOrganizationResponse{}, }, + func() test { + orgID := gofakeit.Name() + return test{ + name: "adding org with same ID twice", + ctx: CTX, + req: &v2beta_org.CreateOrganizationRequest{ + Name: orgID, + Admins: nil, + }, + testfunc: func(ctx context.Context, t *testing.T) { + // create org initally + _, err := Client.CreateOrganization(ctx, &v2beta_org.CreateOrganizationRequest{ + Name: gofakeit.AppName(), + Id: &orgID, + }) + require.NoError(t, err) + }, + wantErr: true, + } + }(), } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { + if tt.testfunc != nil { + tt.testfunc(tt.ctx, t) + } + got, err := Client.CreateOrganization(tt.ctx, tt.req) if tt.wantErr { require.Error(t, err) diff --git a/internal/notification/channels/mock/message.mock.go b/internal/notification/channels/mock/message.mock.go index 1e8ea0067c..d05dc0ff9e 100644 --- a/internal/notification/channels/mock/message.mock.go +++ b/internal/notification/channels/mock/message.mock.go @@ -54,16 +54,16 @@ func (mr *MockMessageMockRecorder) GetContent() *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetContent", reflect.TypeOf((*MockMessage)(nil).GetContent)) } -// GetTriggeringEvent mocks base method. -func (m *MockMessage) GetTriggeringEvent() eventstore.Event { +// GetTriggeringEventType mocks base method. +func (m *MockMessage) GetTriggeringEventType() eventstore.EventType { m.ctrl.T.Helper() - ret := m.ctrl.Call(m, "GetTriggeringEvent") - ret0, _ := ret[0].(eventstore.Event) + ret := m.ctrl.Call(m, "GetTriggeringEventType") + ret0, _ := ret[0].(eventstore.EventType) return ret0 } -// GetTriggeringEvent indicates an expected call of GetTriggeringEvent. -func (mr *MockMessageMockRecorder) GetTriggeringEvent() *gomock.Call { +// GetTriggeringEventType indicates an expected call of GetTriggeringEventType. +func (mr *MockMessageMockRecorder) GetTriggeringEventType() *gomock.Call { mr.mock.ctrl.T.Helper() - return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTriggeringEvent", reflect.TypeOf((*MockMessage)(nil).GetTriggeringEvent)) + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetTriggeringEventType", reflect.TypeOf((*MockMessage)(nil).GetTriggeringEventType)) } diff --git a/internal/repository/org/org.go b/internal/repository/org/org.go index cf8a3ce114..597e7d147f 100644 --- a/internal/repository/org/org.go +++ b/internal/repository/org/org.go @@ -23,6 +23,19 @@ const ( OrgStateSearchField = "state" ) +func NewAddOrgIDUniqueConstraint(orgID string) *eventstore.UniqueConstraint { + return eventstore.NewAddEventUniqueConstraint( + uniqueOrgname, + orgID, + "Errors.Org.AlreadyExists") +} + +func NewRemoveOrgIDUniqueConstraint(orgID string) *eventstore.UniqueConstraint { + return eventstore.NewRemoveUniqueConstraint( + uniqueOrgname, + orgID) +} + func NewAddOrgNameUniqueConstraint(orgName string) *eventstore.UniqueConstraint { return eventstore.NewAddEventUniqueConstraint( uniqueOrgname, @@ -39,6 +52,7 @@ func NewRemoveOrgNameUniqueConstraint(orgName string) *eventstore.UniqueConstrai type OrgAddedEvent struct { eventstore.BaseEvent `json:"-"` + ID string `json:"id,omitempty"` Name string `json:"name,omitempty"` } @@ -47,7 +61,7 @@ func (e *OrgAddedEvent) Payload() interface{} { } func (e *OrgAddedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { - return []*eventstore.UniqueConstraint{NewAddOrgNameUniqueConstraint(e.Name)} + return []*eventstore.UniqueConstraint{NewAddOrgIDUniqueConstraint(e.ID), NewAddOrgNameUniqueConstraint(e.Name)} } func (e *OrgAddedEvent) Fields() []*eventstore.FieldOperation { @@ -94,6 +108,7 @@ func NewOrgAddedEvent(ctx context.Context, aggregate *eventstore.Aggregate, name aggregate, OrgAddedEventType, ), + ID: aggregate.ID, Name: name, } } @@ -276,6 +291,7 @@ func OrgReactivatedEventMapper(event eventstore.Event) (eventstore.Event, error) type OrgRemovedEvent struct { eventstore.BaseEvent `json:"-"` + id string name string usernames []string loginMustBeDomain bool @@ -290,6 +306,7 @@ func (e *OrgRemovedEvent) Payload() interface{} { func (e *OrgRemovedEvent) UniqueConstraints() []*eventstore.UniqueConstraint { constraints := []*eventstore.UniqueConstraint{ + NewRemoveOrgIDUniqueConstraint(e.id), NewRemoveOrgNameUniqueConstraint(e.name), } for _, name := range e.usernames { @@ -321,6 +338,7 @@ func NewOrgRemovedEvent(ctx context.Context, aggregate *eventstore.Aggregate, na aggregate, OrgRemovedEventType, ), + id: aggregate.ID, name: name, usernames: usernames, domains: domains, diff --git a/internal/static/i18n/en.yaml b/internal/static/i18n/en.yaml index 0f512defe4..de019d734e 100644 --- a/internal/static/i18n/en.yaml +++ b/internal/static/i18n/en.yaml @@ -195,7 +195,7 @@ Errors: AlreadyExists: Instance already exists NotChanged: Instance not changed Org: - AlreadyExists: Organisation's name already taken + AlreadyExists: Organisation's name or id already taken Invalid: Organisation is invalid AlreadyDeactivated: Organisation is already deactivated AlreadyActive: Organisation is already active diff --git a/proto/zitadel/org/v2beta/org_service.proto b/proto/zitadel/org/v2beta/org_service.proto index 387b2cb825..01936d84b7 100644 --- a/proto/zitadel/org/v2beta/org_service.proto +++ b/proto/zitadel/org/v2beta/org_service.proto @@ -177,7 +177,7 @@ service OrganizationService { responses: { key: "409" value: { - description: "Organisation's name already taken"; + description: "Organisation's name or id already taken"; } }; };