diff --git a/internal/command/org.go b/internal/command/org.go index d8e567758..ca2165a6d 100644 --- a/internal/command/org.go +++ b/internal/command/org.go @@ -327,6 +327,26 @@ func (c *Commands) getOrg(ctx context.Context, orgID string) (*domain.Org, error return orgWriteModelToOrg(writeModel), nil } +func checkOrgExistsInPreparation(ctx context.Context, filter preparation.FilterToQueryReducer, orgID string) error { + orgWriteModel := NewOrgWriteModel(orgID) + + events, err := filter(ctx, orgWriteModel.Query()) + if err != nil { + return err + } + + orgWriteModel.AppendEvents(events...) + if err = orgWriteModel.Reduce(); err != nil { + return err + } + + if !isOrgStateExists(orgWriteModel.State) { + return zerrors.ThrowPreconditionFailed(nil, "COMMAND-QXPGs", "Errors.Org.NotFound") + } + + return nil +} + func (c *Commands) checkOrgExists(ctx context.Context, orgID string) error { orgWriteModel, err := c.getOrgWriteModelByID(ctx, orgID) if err != nil { diff --git a/internal/command/org_domain.go b/internal/command/org_domain.go index 9e0edfce5..be7f20df2 100644 --- a/internal/command/org_domain.go +++ b/internal/command/org_domain.go @@ -35,7 +35,8 @@ func (c *Commands) prepareAddOrgDomain(a *org.Aggregate, addDomain string, userI return nil, err } } - if err := c.checkOrgExists(ctx, a.ID); err != nil { + + if err := checkOrgExistsInPreparation(ctx, filter, a.ID); err != nil { return nil, err } diff --git a/internal/command/org_domain_test.go b/internal/command/org_domain_test.go index a2cd5dc72..dc331b003 100644 --- a/internal/command/org_domain_test.go +++ b/internal/command/org_domain_test.go @@ -34,6 +34,9 @@ func TestAddDomain(t *testing.T) { } agg := org.NewAggregate("test") + noEvents := func(ctx context.Context, queryFactory *eventstore.SearchQueryBuilder) ([]eventstore.Event, error) { + return nil, nil + } tests := []struct { name string @@ -58,19 +61,47 @@ func TestAddDomain(t *testing.T) { a: agg, domain: "domain", claimedUserIDs: []string{"userID1"}, - filter: func(ctx context.Context, queryFactory *eventstore.SearchQueryBuilder) ([]eventstore.Event, error) { - return []eventstore.Event{ - org.NewDomainPolicyAddedEvent(ctx, &agg.Aggregate, true, true, true), - }, nil + filter: NewMultiFilter(). + Append(func(ctx context.Context, queryFactory *eventstore.SearchQueryBuilder) ([]eventstore.Event, error) { + return []eventstore.Event{ + org.NewOrgAddedEvent(ctx, &agg.Aggregate, "org"), + }, nil + }). + Append(noEvents). + Append(func(ctx context.Context, queryFactory *eventstore.SearchQueryBuilder) ([]eventstore.Event, error) { + return []eventstore.Event{ + org.NewDomainPolicyAddedEvent(ctx, &agg.Aggregate, true, true, true), + }, nil + }). + Filter(), + eventstore: expectEventstore(), + loginPaths: expectLoginPathsNoCall, + }, + want: Want{ + Commands: []eventstore.Command{ + org.NewDomainAddedEvent(context.Background(), &agg.Aggregate, "domain"), }, - eventstore: expectEventstore( - expectFilter( - eventFromEventPusher( - org.NewOrgAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, - "org"), - ), - ), - ), + }, + }, + { + name: "correct when org exists only in the current preparation", + args: args{ + a: agg, + domain: "domain", + filter: NewMultiFilter(). + Append(func(ctx context.Context, queryFactory *eventstore.SearchQueryBuilder) ([]eventstore.Event, error) { + return []eventstore.Event{ + org.NewOrgAddedEvent(ctx, &agg.Aggregate, "org"), + }, nil + }). + Append(noEvents). + Append(func(ctx context.Context, queryFactory *eventstore.SearchQueryBuilder) ([]eventstore.Event, error) { + return []eventstore.Event{ + org.NewDomainPolicyAddedEvent(ctx, &agg.Aggregate, true, true, true), + }, nil + }). + Filter(), + eventstore: expectEventstore(), loginPaths: expectLoginPathsNoCall, }, want: Want{ @@ -86,41 +117,41 @@ func TestAddDomain(t *testing.T) { domain: "domain", claimedUserIDs: []string{"userID1"}, idGenerator: id_mock.ExpectID(t, "newID"), - filter: func() func(ctx context.Context, queryFactory *eventstore.SearchQueryBuilder) ([]eventstore.Event, error) { - i := 0 //TODO: we should fix this in the future to use some kind of mock struct and expect filter calls - return func(ctx context.Context, queryFactory *eventstore.SearchQueryBuilder) ([]eventstore.Event, error) { - if i == 2 { - i++ - return []eventstore.Event{user.NewHumanAddedEvent( - ctx, - &user.NewAggregate("userID1", "org2").Aggregate, - "username", - "firstname", - "lastname", - "nickname", - "displayname", - language.Und, - domain.GenderUnspecified, - "email", - false, - )}, nil - } - if i == 3 { - i++ - return []eventstore.Event{org.NewDomainPolicyAddedEvent(ctx, &agg.Aggregate, false, false, false)}, nil - } - i++ - return []eventstore.Event{org.NewDomainPolicyAddedEvent(ctx, &agg.Aggregate, true, false, false)}, nil - } - }(), - eventstore: expectEventstore( - expectFilter( - eventFromEventPusher( - org.NewOrgAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, - "org"), - ), - ), - ), + filter: NewMultiFilter(). + Append(func(ctx context.Context, queryFactory *eventstore.SearchQueryBuilder) ([]eventstore.Event, error) { + return []eventstore.Event{ + org.NewOrgAddedEvent(ctx, &agg.Aggregate, "org"), + }, nil + }). + Append(noEvents). + Append(func(ctx context.Context, queryFactory *eventstore.SearchQueryBuilder) ([]eventstore.Event, error) { + return []eventstore.Event{ + org.NewDomainPolicyAddedEvent(ctx, &agg.Aggregate, false, false, false), + }, nil + }). + Append(func(ctx context.Context, queryFactory *eventstore.SearchQueryBuilder) ([]eventstore.Event, error) { + return []eventstore.Event{user.NewHumanAddedEvent( + ctx, + &user.NewAggregate("userID1", "org2").Aggregate, + "username", + "firstname", + "lastname", + "nickname", + "displayname", + language.Und, + domain.GenderUnspecified, + "email", + false, + )}, nil + }). + Append(func(ctx context.Context, queryFactory *eventstore.SearchQueryBuilder) ([]eventstore.Event, error) { + return []eventstore.Event{ + org.NewDomainPolicyAddedEvent(ctx, &org.NewAggregate("org2").Aggregate, false, false, false), + }, nil + }). + Append(noEvents). + Filter(), + eventstore: expectEventstore(), loginPaths: expectLoginPathsDefaultDomainClaimedURLTemplate(""), }, want: Want{ @@ -137,21 +168,21 @@ func TestAddDomain(t *testing.T) { a: agg, domain: "domain", claimedUserIDs: []string{"userID1"}, - filter: func(ctx context.Context, queryFactory *eventstore.SearchQueryBuilder) ([]eventstore.Event, error) { - return []eventstore.Event{ - org.NewDomainAddedEvent(ctx, &agg.Aggregate, "domain"), - org.NewDomainVerificationAddedEvent(ctx, &agg.Aggregate, "domain", domain.OrgDomainValidationTypeHTTP, nil), - org.NewDomainVerifiedEvent(ctx, &agg.Aggregate, "domain"), - }, nil - }, - eventstore: expectEventstore( - expectFilter( - eventFromEventPusher( - org.NewOrgAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, - "org"), - ), - ), - ), + filter: NewMultiFilter(). + Append(func(ctx context.Context, queryFactory *eventstore.SearchQueryBuilder) ([]eventstore.Event, error) { + return []eventstore.Event{ + org.NewOrgAddedEvent(ctx, &agg.Aggregate, "org"), + }, nil + }). + Append(func(ctx context.Context, queryFactory *eventstore.SearchQueryBuilder) ([]eventstore.Event, error) { + return []eventstore.Event{ + org.NewDomainAddedEvent(ctx, &agg.Aggregate, "domain"), + org.NewDomainVerificationAddedEvent(ctx, &agg.Aggregate, "domain", domain.OrgDomainValidationTypeHTTP, nil), + org.NewDomainVerifiedEvent(ctx, &agg.Aggregate, "domain"), + }, nil + }). + Filter(), + eventstore: expectEventstore(), loginPaths: expectLoginPathsNoCall, }, want: Want{ @@ -179,19 +210,20 @@ func TestAddDomain(t *testing.T) { a: agg, domain: "domain", claimedUserIDs: []string{"userID1"}, - filter: func(ctx context.Context, queryFactory *eventstore.SearchQueryBuilder) ([]eventstore.Event, error) { - return []eventstore.Event{ - org.NewDomainPolicyAddedEvent(ctx, &agg.Aggregate, true, true, true), - }, nil - }, - eventstore: expectEventstore( - expectFilter( - eventFromEventPusher( - org.NewOrgAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate, - "org"), - ), - ), - ), + filter: NewMultiFilter(). + Append(func(ctx context.Context, queryFactory *eventstore.SearchQueryBuilder) ([]eventstore.Event, error) { + return []eventstore.Event{ + org.NewOrgAddedEvent(ctx, &agg.Aggregate, "org"), + }, nil + }). + Append(noEvents). + Append(func(ctx context.Context, queryFactory *eventstore.SearchQueryBuilder) ([]eventstore.Event, error) { + return []eventstore.Event{ + org.NewDomainPolicyAddedEvent(ctx, &agg.Aggregate, true, true, true), + }, nil + }). + Filter(), + eventstore: expectEventstore(), loginPaths: expectLoginPathsNoCall, permissionCheck: newMockOrganizationPermissionCheckAllowed(), }, diff --git a/internal/command/org_test.go b/internal/command/org_test.go index 71bdd1f5a..b9605fbb3 100644 --- a/internal/command/org_test.go +++ b/internal/command/org_test.go @@ -18,6 +18,7 @@ import ( "github.com/zitadel/zitadel/internal/eventstore/v1/models" "github.com/zitadel/zitadel/internal/id" id_mock "github.com/zitadel/zitadel/internal/id/mock" + "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/repository/org" "github.com/zitadel/zitadel/internal/repository/project" "github.com/zitadel/zitadel/internal/repository/user" @@ -1845,6 +1846,64 @@ func TestCommandSide_SetUpOrg(t *testing.T) { }, }, }, + { + name: "no admin added, custom domain", + fields: fields{ + eventstore: expectEventstore( + expectFilter(), // org already exists check + expectFilter(), // org exists in current preparation + expectFilter(), // custom domain does not exist yet + expectFilter(), // no org domain policy yet + expectFilter( + eventFromEventPusher( + instance.NewDomainPolicyAddedEvent(context.Background(), + &instance.NewAggregate("instance1").Aggregate, + true, + true, + true, + ), + ), + ), + expectPush( + eventFromEventPusher(org.NewOrgAddedEvent(context.Background(), + &org.NewAggregate("orgID").Aggregate, + "Org", + )), + eventFromEventPusher(org.NewDomainAddedEvent(context.Background(), + &org.NewAggregate("orgID").Aggregate, "org.iam-domain", + )), + eventFromEventPusher(org.NewDomainVerifiedEvent(context.Background(), + &org.NewAggregate("orgID").Aggregate, + "org.iam-domain", + )), + eventFromEventPusher(org.NewDomainPrimarySetEvent(context.Background(), + &org.NewAggregate("orgID").Aggregate, + "org.iam-domain", + )), + eventFromEventPusher(org.NewDomainAddedEvent(context.Background(), + &org.NewAggregate("orgID").Aggregate, + "custom.example.com", + )), + ), + ), + idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "orgID"), + }, + args: args{ + ctx: http_util.WithRequestedHost(authz.WithInstanceID(context.Background(), "instance1"), "iam-domain"), + setupOrg: &OrgSetup{ + Name: "Org", + CustomDomain: "custom.example.com", + }, + }, + res: res{ + createdOrg: &CreatedOrg{ + ObjectDetails: &domain.ObjectDetails{ + ResourceOwner: "orgID", + }, + OrgAdmins: []OrgAdmin{}, + }, + }, + }, { name: "existing human added", fields: fields{