fix: uniqueness (#1710)

* fix: uniqueconstraint to lower

* feat: change org

* feat: org change test

* feat: change org

* fix: tests

* fix: handle domain claims correctly

* feat: update org

Co-authored-by: fabi <fabienne.gerschwiler@gmail.com>
This commit is contained in:
Livio Amstutz
2021-05-04 11:09:24 +02:00
committed by GitHub
parent 667cc30291
commit 87c1dfa3aa
28 changed files with 607 additions and 50 deletions

View File

@@ -30,7 +30,7 @@ func (c *Commands) checkOrgExists(ctx context.Context, orgID string) error {
return nil
}
func (c *Commands) SetUpOrg(ctx context.Context, organisation *domain.Org, admin *domain.Human) (*domain.ObjectDetails, error) {
func (c *Commands) SetUpOrg(ctx context.Context, organisation *domain.Org, admin *domain.Human, claimedUserIDs []string) (*domain.ObjectDetails, error) {
orgIAMPolicy, err := c.getDefaultOrgIAMPolicy(ctx)
if err != nil {
return nil, caos_errs.ThrowPreconditionFailed(err, "COMMAND-33M9f", "Errors.IAM.OrgIAMPolicy.NotFound")
@@ -39,7 +39,7 @@ func (c *Commands) SetUpOrg(ctx context.Context, organisation *domain.Org, admin
if err != nil {
return nil, caos_errs.ThrowPreconditionFailed(err, "COMMAND-M5Fsd", "Errors.IAM.PasswordComplexity.NotFound")
}
_, orgWriteModel, _, _, events, err := c.setUpOrg(ctx, organisation, admin, orgIAMPolicy, pwPolicy)
_, orgWriteModel, _, _, events, err := c.setUpOrg(ctx, organisation, admin, orgIAMPolicy, pwPolicy, claimedUserIDs)
if err != nil {
return nil, err
}
@@ -54,8 +54,8 @@ func (c *Commands) SetUpOrg(ctx context.Context, organisation *domain.Org, admin
return writeModelToObjectDetails(&orgWriteModel.WriteModel), nil
}
func (c *Commands) AddOrg(ctx context.Context, name, userID, resourceOwner string) (*domain.Org, error) {
orgAgg, addedOrg, events, err := c.addOrg(ctx, &domain.Org{Name: name})
func (c *Commands) AddOrg(ctx context.Context, name, userID, resourceOwner string, claimedUserIDs []string) (*domain.Org, error) {
orgAgg, addedOrg, events, err := c.addOrg(ctx, &domain.Org{Name: name}, claimedUserIDs)
if err != nil {
return nil, err
}
@@ -80,6 +80,41 @@ func (c *Commands) AddOrg(ctx context.Context, name, userID, resourceOwner strin
return orgWriteModelToOrg(addedOrg), nil
}
func (c *Commands) ChangeOrg(ctx context.Context, orgID, name string) (*domain.ObjectDetails, error) {
if orgID == "" || name == "" {
return nil, caos_errs.ThrowInvalidArgument(nil, "EVENT-Mf9sd", "Errors.Org.Invalid")
}
orgWriteModel, err := c.getOrgWriteModelByID(ctx, orgID)
if err != nil {
return nil, err
}
if orgWriteModel.State == domain.OrgStateUnspecified || orgWriteModel.State == domain.OrgStateRemoved {
return nil, caos_errs.ThrowNotFound(nil, "ORG-1MRds", "Errors.Org.NotFound")
}
if orgWriteModel.Name == name {
return nil, caos_errs.ThrowNotFound(nil, "ORG-4VSdf", "Errors.Org.NotChanged")
}
orgAgg := OrgAggregateFromWriteModel(&orgWriteModel.WriteModel)
events := make([]eventstore.EventPusher, 0)
events = append(events, org.NewOrgChangedEvent(ctx, orgAgg, orgWriteModel.Name, name))
changeDomainEvents, err := c.changeDefaultDomain(ctx, orgID, name)
if err != nil {
return nil, err
}
if len(changeDomainEvents) > 0 {
events = append(events, changeDomainEvents...)
}
pushedEvents, err := c.eventstore.PushEvents(ctx, events...)
if err != nil {
return nil, err
}
err = AppendAndReduce(orgWriteModel, pushedEvents...)
if err != nil {
return nil, err
}
return writeModelToObjectDetails(&orgWriteModel.WriteModel), nil
}
func (c *Commands) DeactivateOrg(ctx context.Context, orgID string) (*domain.ObjectDetails, error) {
orgWriteModel, err := c.getOrgWriteModelByID(ctx, orgID)
if err != nil {
@@ -126,8 +161,8 @@ func (c *Commands) ReactivateOrg(ctx context.Context, orgID string) (*domain.Obj
return writeModelToObjectDetails(&orgWriteModel.WriteModel), nil
}
func (c *Commands) setUpOrg(ctx context.Context, organisation *domain.Org, admin *domain.Human, loginPolicy *domain.OrgIAMPolicy, pwPolicy *domain.PasswordComplexityPolicy) (orgAgg *eventstore.Aggregate, org *OrgWriteModel, human *HumanWriteModel, orgMember *OrgMemberWriteModel, events []eventstore.EventPusher, err error) {
orgAgg, orgWriteModel, addOrgEvents, err := c.addOrg(ctx, organisation)
func (c *Commands) setUpOrg(ctx context.Context, organisation *domain.Org, admin *domain.Human, loginPolicy *domain.OrgIAMPolicy, pwPolicy *domain.PasswordComplexityPolicy, claimedUserIDs []string) (orgAgg *eventstore.Aggregate, org *OrgWriteModel, human *HumanWriteModel, orgMember *OrgMemberWriteModel, events []eventstore.EventPusher, err error) {
orgAgg, orgWriteModel, addOrgEvents, err := c.addOrg(ctx, organisation, claimedUserIDs)
if err != nil {
return nil, nil, nil, nil, nil, err
}
@@ -148,7 +183,7 @@ func (c *Commands) setUpOrg(ctx context.Context, organisation *domain.Org, admin
return orgAgg, orgWriteModel, human, addedMember, addOrgEvents, nil
}
func (c *Commands) addOrg(ctx context.Context, organisation *domain.Org, claimedUserIDs ...string) (_ *eventstore.Aggregate, _ *OrgWriteModel, _ []eventstore.EventPusher, err error) {
func (c *Commands) addOrg(ctx context.Context, organisation *domain.Org, claimedUserIDs []string) (_ *eventstore.Aggregate, _ *OrgWriteModel, _ []eventstore.EventPusher, err error) {
if organisation == nil || !organisation.IsValid() {
return nil, nil, nil, caos_errs.ThrowInvalidArgument(nil, "COMM-deLSk", "Errors.Org.Invalid")
}
@@ -165,7 +200,7 @@ func (c *Commands) addOrg(ctx context.Context, organisation *domain.Org, claimed
org.NewOrgAddedEvent(ctx, orgAgg, organisation.Name),
}
for _, orgDomain := range organisation.Domains {
orgDomainEvents, err := c.addOrgDomain(ctx, orgAgg, NewOrgDomainWriteModel(orgAgg.ID, orgDomain.Domain), orgDomain, claimedUserIDs...)
orgDomainEvents, err := c.addOrgDomain(ctx, orgAgg, NewOrgDomainWriteModel(orgAgg.ID, orgDomain.Domain), orgDomain, claimedUserIDs)
if err != nil {
return nil, nil, nil, err
} else {

View File

@@ -13,13 +13,13 @@ import (
"github.com/caos/zitadel/internal/repository/org"
)
func (c *Commands) AddOrgDomain(ctx context.Context, orgDomain *domain.OrgDomain) (*domain.OrgDomain, error) {
func (c *Commands) AddOrgDomain(ctx context.Context, orgDomain *domain.OrgDomain, claimedUserIDs []string) (*domain.OrgDomain, error) {
if !orgDomain.IsValid() {
return nil, caos_errs.ThrowInvalidArgument(nil, "ORG-R24hb", "Errors.Org.InvalidDomain")
}
domainWriteModel := NewOrgDomainWriteModel(orgDomain.AggregateID, orgDomain.Domain)
orgAgg := OrgAggregateFromWriteModel(&domainWriteModel.WriteModel)
events, err := c.addOrgDomain(ctx, orgAgg, domainWriteModel, orgDomain)
events, err := c.addOrgDomain(ctx, orgAgg, domainWriteModel, orgDomain, claimedUserIDs)
if err != nil {
return nil, err
}
@@ -72,7 +72,7 @@ func (c *Commands) GenerateOrgDomainValidation(ctx context.Context, orgDomain *d
return token, url, nil
}
func (c *Commands) ValidateOrgDomain(ctx context.Context, orgDomain *domain.OrgDomain, claimedUserIDs ...string) (*domain.ObjectDetails, error) {
func (c *Commands) ValidateOrgDomain(ctx context.Context, orgDomain *domain.OrgDomain, claimedUserIDs []string) (*domain.ObjectDetails, error) {
if orgDomain == nil || !orgDomain.IsValid() || orgDomain.AggregateID == "" {
return nil, caos_errs.ThrowInvalidArgument(nil, "ORG-R24hb", "Errors.Org.InvalidDomain")
}
@@ -177,7 +177,7 @@ func (c *Commands) RemoveOrgDomain(ctx context.Context, orgDomain *domain.OrgDom
return writeModelToObjectDetails(&domainWriteModel.WriteModel), nil
}
func (c *Commands) addOrgDomain(ctx context.Context, orgAgg *eventstore.Aggregate, addedDomain *OrgDomainWriteModel, orgDomain *domain.OrgDomain, claimedUserIDs ...string) ([]eventstore.EventPusher, error) {
func (c *Commands) addOrgDomain(ctx context.Context, orgAgg *eventstore.Aggregate, addedDomain *OrgDomainWriteModel, orgDomain *domain.OrgDomain, claimedUserIDs []string) ([]eventstore.EventPusher, error) {
err := c.eventstore.FilterToQueryReducer(ctx, addedDomain)
if err != nil {
return nil, err
@@ -207,6 +207,34 @@ func (c *Commands) addOrgDomain(ctx context.Context, orgAgg *eventstore.Aggregat
return events, nil
}
func (c *Commands) changeDefaultDomain(ctx context.Context, orgID, newName string) ([]eventstore.EventPusher, error) {
orgDomains := NewOrgDomainsWriteModel(orgID)
err := c.eventstore.FilterToQueryReducer(ctx, orgDomains)
if err != nil {
return nil, err
}
defaultDomain := domain.NewIAMDomainName(orgDomains.OrgName, c.iamDomain)
isPrimary := defaultDomain == orgDomains.PrimaryDomain
orgAgg := OrgAggregateFromWriteModel(&orgDomains.WriteModel)
for _, orgDomain := range orgDomains.Domains {
if orgDomain.State == domain.OrgDomainStateActive {
if orgDomain.Domain == defaultDomain {
newDefaultDomain := domain.NewIAMDomainName(newName, c.iamDomain)
events := []eventstore.EventPusher{
org.NewDomainAddedEvent(ctx, orgAgg, newDefaultDomain),
org.NewDomainVerifiedEvent(ctx, orgAgg, newDefaultDomain),
}
if isPrimary {
events = append(events, org.NewDomainPrimarySetEvent(ctx, orgAgg, newDefaultDomain))
}
events = append(events, org.NewDomainRemovedEvent(ctx, orgAgg, orgDomain.Domain, orgDomain.Verified))
return events, nil
}
}
}
return nil, nil
}
func (c *Commands) removeCustomDomains(ctx context.Context, orgID string) ([]eventstore.EventPusher, error) {
orgDomains := NewOrgDomainsWriteModel(orgID)
err := c.eventstore.FilterToQueryReducer(ctx, orgDomains)

View File

@@ -26,8 +26,9 @@ func TestCommandSide_AddOrgDomain(t *testing.T) {
eventstore *eventstore.Eventstore
}
type args struct {
ctx context.Context
domain *domain.OrgDomain
ctx context.Context
domain *domain.OrgDomain
claimedUserIDs []string
}
type res struct {
want *domain.OrgDomain
@@ -129,7 +130,7 @@ func TestCommandSide_AddOrgDomain(t *testing.T) {
r := &Commands{
eventstore: tt.fields.eventstore,
}
got, err := r.AddOrgDomain(tt.args.ctx, tt.args.domain)
got, err := r.AddOrgDomain(tt.args.ctx, tt.args.domain, tt.args.claimedUserIDs)
if tt.res.err == nil {
assert.NoError(t, err)
}
@@ -864,7 +865,7 @@ func TestCommandSide_ValidateOrgDomain(t *testing.T) {
iamDomain: "zitadel.ch",
idGenerator: tt.fields.idGenerator,
}
got, err := r.ValidateOrgDomain(tt.args.ctx, tt.args.domain, tt.args.claimedUserIDs...)
got, err := r.ValidateOrgDomain(tt.args.ctx, tt.args.domain, tt.args.claimedUserIDs)
if tt.res.err == nil {
assert.NoError(t, err)
}

View File

@@ -28,10 +28,11 @@ func TestCommandSide_AddOrg(t *testing.T) {
zitadelRoles []authz.RoleMapping
}
type args struct {
ctx context.Context
name string
userID string
resourceOwner string
ctx context.Context
name string
userID string
resourceOwner string
claimedUserIDs []string
}
type res struct {
want *domain.Org
@@ -326,7 +327,7 @@ func TestCommandSide_AddOrg(t *testing.T) {
iamDomain: tt.fields.iamDomain,
zitadelRoles: tt.fields.zitadelRoles,
}
got, err := r.AddOrg(tt.args.ctx, tt.args.name, tt.args.userID, tt.args.resourceOwner)
got, err := r.AddOrg(tt.args.ctx, tt.args.name, tt.args.userID, tt.args.resourceOwner, tt.args.claimedUserIDs)
if tt.res.err == nil {
assert.NoError(t, err)
}
@@ -340,6 +341,227 @@ func TestCommandSide_AddOrg(t *testing.T) {
}
}
func TestCommandSide_ChangeOrg(t *testing.T) {
type fields struct {
eventstore *eventstore.Eventstore
iamDomain string
}
type args struct {
ctx context.Context
orgID string
name string
}
type res struct {
want *domain.Org
err func(error) bool
}
tests := []struct {
name string
fields fields
args args
res res
}{
{
name: "empty name, invalid argument error",
fields: fields{
eventstore: eventstoreExpect(
t,
),
},
args: args{
ctx: context.Background(),
orgID: "org1",
},
res: res{
err: caos_errs.IsErrorInvalidArgument,
},
},
{
name: "org not found, error",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(),
),
},
args: args{
ctx: context.Background(),
orgID: "org1",
name: "org",
},
res: res{
err: caos_errs.IsNotFound,
},
},
{
name: "push failed, error",
fields: fields{
iamDomain: "zitadel.ch",
eventstore: eventstoreExpect(
t,
expectFilter(
eventFromEventPusher(
org.NewOrgAddedEvent(context.Background(),
&org.NewAggregate("org1", "org1").Aggregate,
"org"),
),
),
expectFilter(),
expectPushFailed(
caos_errs.ThrowInternal(nil, "id", "message"),
[]*repository.Event{
eventFromEventPusher(org.NewOrgChangedEvent(context.Background(),
&org.NewAggregate("org1", "org1").Aggregate, "org", "neworg")),
},
uniqueConstraintsFromEventConstraint(org.NewRemoveOrgNameUniqueConstraint("org")),
uniqueConstraintsFromEventConstraint(org.NewAddOrgNameUniqueConstraint("neworg")),
),
),
},
args: args{
ctx: context.Background(),
orgID: "org1",
name: "neworg",
},
res: res{
err: caos_errs.IsInternal,
},
},
{
name: "change org name verified, not primary",
fields: fields{
iamDomain: "zitadel.ch",
eventstore: eventstoreExpect(
t,
expectFilter(
eventFromEventPusher(
org.NewOrgAddedEvent(context.Background(),
&org.NewAggregate("org1", "org1").Aggregate,
"org"),
),
),
expectFilter(
eventFromEventPusher(
org.NewOrgAddedEvent(context.Background(),
&org.NewAggregate("org1", "org1").Aggregate,
"org"),
),
eventFromEventPusher(
org.NewDomainAddedEvent(context.Background(),
&org.NewAggregate("org1", "org1").Aggregate,
"org.zitadel.ch"),
),
eventFromEventPusher(
org.NewDomainVerifiedEvent(context.Background(),
&org.NewAggregate("org1", "org1").Aggregate,
"org.zitadel.ch"),
),
),
expectPush(
[]*repository.Event{
eventFromEventPusher(org.NewOrgChangedEvent(context.Background(),
&org.NewAggregate("org1", "org1").Aggregate, "org", "neworg")),
eventFromEventPusher(org.NewDomainAddedEvent(context.Background(),
&org.NewAggregate("org1", "org1").Aggregate, "neworg.zitadel.ch")),
eventFromEventPusher(org.NewDomainVerifiedEvent(context.Background(),
&org.NewAggregate("org1", "org1").Aggregate, "neworg.zitadel.ch")),
eventFromEventPusher(org.NewDomainRemovedEvent(context.Background(),
&org.NewAggregate("org1", "org1").Aggregate, "org.zitadel.ch", true)),
},
uniqueConstraintsFromEventConstraint(org.NewRemoveOrgNameUniqueConstraint("org")),
uniqueConstraintsFromEventConstraint(org.NewAddOrgNameUniqueConstraint("neworg")),
uniqueConstraintsFromEventConstraint(org.NewAddOrgDomainUniqueConstraint("neworg.zitadel.ch")),
uniqueConstraintsFromEventConstraint(org.NewRemoveOrgDomainUniqueConstraint("org.zitadel.ch")),
),
),
},
args: args{
ctx: context.Background(),
orgID: "org1",
name: "neworg",
},
res: res{},
},
{
name: "change org name verified, with primary",
fields: fields{
iamDomain: "zitadel.ch",
eventstore: eventstoreExpect(
t,
expectFilter(
eventFromEventPusher(
org.NewOrgAddedEvent(context.Background(),
&org.NewAggregate("org1", "org1").Aggregate,
"org"),
),
),
expectFilter(
eventFromEventPusher(
org.NewOrgAddedEvent(context.Background(),
&org.NewAggregate("org1", "org1").Aggregate,
"org"),
),
eventFromEventPusher(
org.NewDomainAddedEvent(context.Background(),
&org.NewAggregate("org1", "org1").Aggregate,
"org.zitadel.ch"),
),
eventFromEventPusher(
org.NewDomainVerifiedEvent(context.Background(),
&org.NewAggregate("org1", "org1").Aggregate,
"org.zitadel.ch"),
),
eventFromEventPusher(
org.NewDomainPrimarySetEvent(context.Background(),
&org.NewAggregate("org1", "org1").Aggregate,
"org.zitadel.ch"),
),
),
expectPush(
[]*repository.Event{
eventFromEventPusher(org.NewOrgChangedEvent(context.Background(),
&org.NewAggregate("org1", "org1").Aggregate, "org", "neworg")),
eventFromEventPusher(org.NewDomainAddedEvent(context.Background(),
&org.NewAggregate("org1", "org1").Aggregate, "neworg.zitadel.ch")),
eventFromEventPusher(org.NewDomainVerifiedEvent(context.Background(),
&org.NewAggregate("org1", "org1").Aggregate, "neworg.zitadel.ch")),
eventFromEventPusher(org.NewDomainPrimarySetEvent(context.Background(),
&org.NewAggregate("org1", "org1").Aggregate, "neworg.zitadel.ch")),
eventFromEventPusher(org.NewDomainRemovedEvent(context.Background(),
&org.NewAggregate("org1", "org1").Aggregate, "org.zitadel.ch", true)),
},
uniqueConstraintsFromEventConstraint(org.NewRemoveOrgNameUniqueConstraint("org")),
uniqueConstraintsFromEventConstraint(org.NewAddOrgNameUniqueConstraint("neworg")),
uniqueConstraintsFromEventConstraint(org.NewAddOrgDomainUniqueConstraint("neworg.zitadel.ch")),
uniqueConstraintsFromEventConstraint(org.NewRemoveOrgDomainUniqueConstraint("org.zitadel.ch")),
),
),
},
args: args{
ctx: context.Background(),
orgID: "org1",
name: "neworg",
},
res: res{},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := &Commands{
eventstore: tt.fields.eventstore,
iamDomain: tt.fields.iamDomain,
}
_, err := r.ChangeOrg(tt.args.ctx, tt.args.orgID, tt.args.name)
if tt.res.err == nil {
assert.NoError(t, err)
}
if tt.res.err != nil && !tt.res.err(err) {
t.Errorf("got wrong err: %v ", err)
}
})
}
}
func TestCommandSide_DeactivateOrg(t *testing.T) {
type fields struct {
eventstore *eventstore.Eventstore

View File

@@ -134,7 +134,7 @@ func (c *Commands) SetupStep1(ctx context.Context, step1 *Step1) error {
EmailAddress: organisation.Owner.Email,
IsEmailVerified: true,
},
}, orgIAMPolicy, pwPolicy)
}, orgIAMPolicy, pwPolicy, nil)
if err != nil {
return err
}