fix: multiple setup steps (#773)

* fix: multiple setup steps

* fix: test set up started

* fix: possible nil pointers in setup

* fix: validate executed step
This commit is contained in:
Silvan
2020-09-24 11:38:28 +02:00
committed by GitHub
parent 0bd27bc8e4
commit 3e1204524e
20 changed files with 10036 additions and 18037 deletions

View File

@@ -4,12 +4,22 @@ import (
es_models "github.com/caos/zitadel/internal/eventstore/models"
)
type Step int
const (
Step1 Step = iota + 1
//TODO: label policy
// Step2
//StepCount marks the the length of possible steps (StepCount-1 == last possible step)
StepCount
)
type IAM struct {
es_models.ObjectRoot
GlobalOrgID string
IAMProjectID string
SetUpDone bool
SetUpStarted bool
SetUpDone Step
SetUpStarted Step
Members []*IAMMember
IDPs []*IDPConfig
DefaultLoginPolicy *LoginPolicy

View File

@@ -60,32 +60,37 @@ func (es *IAMEventstore) IAMByID(ctx context.Context, id string) (*iam_model.IAM
return model.IAMToModel(iam), nil
}
func (es *IAMEventstore) StartSetup(ctx context.Context, iamID string) (*iam_model.IAM, error) {
func (es *IAMEventstore) StartSetup(ctx context.Context, iamID string, step iam_model.Step) (*iam_model.IAM, error) {
iam, err := es.IAMByID(ctx, iamID)
if err != nil && !caos_errs.IsNotFound(err) {
return nil, err
}
if iam != nil && iam.SetUpStarted {
if iam != nil && (iam.SetUpStarted >= step || iam.SetUpStarted != iam.SetUpDone) {
return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-9so34", "Setup already started")
}
repoIam := &model.IAM{ObjectRoot: models.ObjectRoot{AggregateID: iamID}}
createAggregate := IAMSetupStartedAggregate(es.AggregateCreator(), repoIam)
err = es_sdk.Push(ctx, es.PushAggregates, repoIam.AppendEvents, createAggregate)
repoIAM := &model.IAM{ObjectRoot: models.ObjectRoot{AggregateID: iamID}, SetUpStarted: model.Step(step)}
if iam != nil {
repoIAM.ObjectRoot = iam.ObjectRoot
}
createAggregate := IAMSetupStartedAggregate(es.AggregateCreator(), repoIAM)
err = es_sdk.Push(ctx, es.PushAggregates, repoIAM.AppendEvents, createAggregate)
if err != nil {
return nil, err
}
es.iamCache.cacheIAM(repoIam)
return model.IAMToModel(repoIam), nil
es.iamCache.cacheIAM(repoIAM)
return model.IAMToModel(repoIAM), nil
}
func (es *IAMEventstore) SetupDone(ctx context.Context, iamID string) (*iam_model.IAM, error) {
func (es *IAMEventstore) SetupDone(ctx context.Context, iamID string, step iam_model.Step) (*iam_model.IAM, error) {
iam, err := es.IAMByID(ctx, iamID)
if err != nil {
return nil, err
}
iam.SetUpDone = step
repoIam := model.IAMFromModel(iam)
createAggregate := IAMSetupDoneAggregate(es.AggregateCreator(), repoIam)
err = es_sdk.Push(ctx, es.PushAggregates, repoIam.AppendEvents, createAggregate)

View File

@@ -79,9 +79,10 @@ func TestSetUpStarted(t *testing.T) {
es *IAMEventstore
ctx context.Context
iamID string
step iam_model.Step
}
type res struct {
iam *model.IAM
iam *iam_model.IAM
errFunc func(err error) bool
}
tests := []struct {
@@ -95,9 +96,10 @@ func TestSetUpStarted(t *testing.T) {
es: GetMockManipulateIamNotExisting(ctrl),
ctx: authz.NewMockContext("orgID", "userID"),
iamID: "iamID",
step: iam_model.Step1,
},
res: res{
iam: &model.IAM{ObjectRoot: es_models.ObjectRoot{AggregateID: "iamID", Sequence: 1}, SetUpStarted: true},
iam: &iam_model.IAM{ObjectRoot: es_models.ObjectRoot{AggregateID: "iamID", Sequence: 1}, SetUpStarted: iam_model.Step1},
},
},
{
@@ -106,6 +108,7 @@ func TestSetUpStarted(t *testing.T) {
es: GetMockManipulateIam(ctrl),
ctx: authz.NewMockContext("orgID", "userID"),
iamID: "iamID",
step: iam_model.Step1,
},
res: res{
errFunc: caos_errs.IsPreconditionFailed,
@@ -114,8 +117,9 @@ func TestSetUpStarted(t *testing.T) {
{
name: "setup iam no id",
args: args{
es: GetMockManipulateIam(ctrl),
ctx: authz.NewMockContext("orgID", "userID"),
es: GetMockManipulateIam(ctrl),
ctx: authz.NewMockContext("orgID", "userID"),
step: iam_model.Step1,
},
res: res{
errFunc: caos_errs.IsPreconditionFailed,
@@ -124,7 +128,7 @@ func TestSetUpStarted(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result, err := tt.args.es.StartSetup(tt.args.ctx, tt.args.iamID)
result, err := tt.args.es.StartSetup(tt.args.ctx, tt.args.iamID, tt.args.step)
if tt.res.errFunc == nil && result.AggregateID == "" {
t.Errorf("result has no id")
@@ -145,9 +149,10 @@ func TestSetUpDone(t *testing.T) {
es *IAMEventstore
ctx context.Context
iamID string
step iam_model.Step
}
type res struct {
iam *model.IAM
iam *iam_model.IAM
errFunc func(err error) bool
}
tests := []struct {
@@ -161,16 +166,18 @@ func TestSetUpDone(t *testing.T) {
es: GetMockManipulateIam(ctrl),
ctx: authz.NewMockContext("orgID", "userID"),
iamID: "iamID",
step: iam_model.Step1,
},
res: res{
iam: &model.IAM{ObjectRoot: es_models.ObjectRoot{AggregateID: "iamID", Sequence: 1}, SetUpStarted: true, SetUpDone: true},
iam: &iam_model.IAM{ObjectRoot: es_models.ObjectRoot{AggregateID: "iamID", Sequence: 1}, SetUpStarted: iam_model.Step1, SetUpDone: iam_model.Step1},
},
},
{
name: "setup iam no id",
args: args{
es: GetMockManipulateIam(ctrl),
ctx: authz.NewMockContext("orgID", "userID"),
es: GetMockManipulateIam(ctrl),
ctx: authz.NewMockContext("orgID", "userID"),
step: iam_model.Step1,
},
res: res{
errFunc: caos_errs.IsPreconditionFailed,
@@ -182,6 +189,7 @@ func TestSetUpDone(t *testing.T) {
es: GetMockManipulateIamNotExisting(ctrl),
ctx: authz.NewMockContext("orgID", "userID"),
iamID: "iamID",
step: iam_model.Step1,
},
res: res{
errFunc: caos_errs.IsNotFound,
@@ -190,7 +198,7 @@ func TestSetUpDone(t *testing.T) {
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result, err := tt.args.es.SetupDone(tt.args.ctx, tt.args.iamID)
result, err := tt.args.es.SetupDone(tt.args.ctx, tt.args.iamID, tt.args.step)
if tt.res.errFunc == nil && result.AggregateID == "" {
t.Errorf("result has no id")
@@ -231,7 +239,7 @@ func TestSetGlobalOrg(t *testing.T) {
globalOrg: "globalOrg",
},
res: res{
iam: &model.IAM{ObjectRoot: es_models.ObjectRoot{AggregateID: "iamID", Sequence: 1}, SetUpStarted: true, GlobalOrgID: "globalOrg"},
iam: &model.IAM{ObjectRoot: es_models.ObjectRoot{AggregateID: "iamID", Sequence: 1}, SetUpStarted: model.Step1, GlobalOrgID: "globalOrg"},
},
},
{
@@ -312,7 +320,7 @@ func TestSetIamProjectID(t *testing.T) {
iamProjectID: "iamProjectID",
},
res: res{
iam: &model.IAM{ObjectRoot: es_models.ObjectRoot{AggregateID: "iamID", Sequence: 1}, SetUpStarted: true, IAMProjectID: "iamProjectID"},
iam: &model.IAM{ObjectRoot: es_models.ObjectRoot{AggregateID: "iamID", Sequence: 1}, SetUpStarted: model.Step1, IAMProjectID: "iamProjectID"},
},
},
{

View File

@@ -43,7 +43,7 @@ func IAMSetupStartedAggregate(aggCreator *es_models.AggregateCreator, iam *model
if err != nil {
return nil, err
}
return agg.AppendEvent(model.IAMSetupStarted, nil)
return agg.AppendEvent(model.IAMSetupStarted, &struct{ Step model.Step }{Step: iam.SetUpStarted})
}
}
@@ -54,7 +54,7 @@ func IAMSetupDoneAggregate(aggCreator *es_models.AggregateCreator, iam *model.IA
return nil, err
}
return agg.AppendEvent(model.IAMSetupDone, nil)
return agg.AppendEvent(model.IAMSetupDone, &struct{ Step model.Step }{Step: iam.SetUpDone})
}
}

View File

@@ -2,6 +2,7 @@ package model
import (
"encoding/json"
"github.com/caos/logging"
caos_errs "github.com/caos/zitadel/internal/errors"
es_models "github.com/caos/zitadel/internal/eventstore/models"
@@ -12,10 +13,19 @@ const (
IAMVersion = "v1"
)
type Step int
const (
Step1 = Step(model.Step1)
//TODO: label policy
// Step2 = Step(model.Step2)
StepCount = Step(model.StepCount)
)
type IAM struct {
es_models.ObjectRoot
SetUpStarted bool `json:"-"`
SetUpDone bool `json:"-"`
SetUpStarted Step `json:"-"`
SetUpDone Step `json:"-"`
GlobalOrgID string `json:"globalOrgId,omitempty"`
IAMProjectID string `json:"iamProjectId,omitempty"`
Members []*IAMMember `json:"-"`
@@ -28,8 +38,8 @@ func IAMFromModel(iam *model.IAM) *IAM {
idps := IDPConfigsFromModel(iam.IDPs)
converted := &IAM{
ObjectRoot: iam.ObjectRoot,
SetUpStarted: iam.SetUpStarted,
SetUpDone: iam.SetUpDone,
SetUpStarted: Step(iam.SetUpStarted),
SetUpDone: Step(iam.SetUpDone),
GlobalOrgID: iam.GlobalOrgID,
IAMProjectID: iam.IAMProjectID,
Members: members,
@@ -46,8 +56,8 @@ func IAMToModel(iam *IAM) *model.IAM {
idps := IDPConfigsToModel(iam.IDPs)
converted := &model.IAM{
ObjectRoot: iam.ObjectRoot,
SetUpStarted: iam.SetUpStarted,
SetUpDone: iam.SetUpDone,
SetUpStarted: model.Step(iam.SetUpStarted),
SetUpDone: model.Step(iam.SetUpDone),
GlobalOrgID: iam.GlobalOrgID,
IAMProjectID: iam.IAMProjectID,
Members: members,
@@ -72,9 +82,27 @@ func (i *IAM) AppendEvent(event *es_models.Event) (err error) {
i.ObjectRoot.AppendEvent(event)
switch event.Type {
case IAMSetupStarted:
i.SetUpStarted = true
if len(event.Data) == 0 {
i.SetUpStarted = Step(model.Step1)
return
}
step := new(struct{ Step Step })
err = json.Unmarshal(event.Data, step)
if err != nil {
return err
}
i.SetUpStarted = step.Step
case IAMSetupDone:
i.SetUpDone = true
if len(event.Data) == 0 {
i.SetUpDone = Step(model.Step1)
return
}
step := new(struct{ Step Step })
err = json.Unmarshal(event.Data, step)
if err != nil {
return err
}
i.SetUpDone = step.Step
case IAMProjectSet,
GlobalOrgSet:
err = i.SetData(event)

View File

@@ -2,8 +2,9 @@ package model
import (
"encoding/json"
es_models "github.com/caos/zitadel/internal/eventstore/models"
"testing"
es_models "github.com/caos/zitadel/internal/eventstore/models"
)
func mockIamData(iam *IAM) []byte {
@@ -27,31 +28,31 @@ func TestProjectRoleAppendEvent(t *testing.T) {
event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: IAMSetupStarted, ResourceOwner: "OrgID"},
iam: &IAM{},
},
result: &IAM{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID"}, SetUpStarted: true},
result: &IAM{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID"}, SetUpStarted: Step1},
},
{
name: "append set up done event",
args: args{
event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: IAMSetupDone, ResourceOwner: "OrgID"},
iam: &IAM{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID"}, SetUpStarted: true},
iam: &IAM{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID"}, SetUpStarted: Step1},
},
result: &IAM{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID"}, SetUpStarted: true, SetUpDone: true},
result: &IAM{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID"}, SetUpStarted: Step1, SetUpDone: Step1},
},
{
name: "append globalorg event",
args: args{
event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: GlobalOrgSet, ResourceOwner: "OrgID", Data: mockIamData(&IAM{GlobalOrgID: "GlobalOrg"})},
iam: &IAM{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID"}, SetUpStarted: true},
iam: &IAM{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID"}, SetUpStarted: Step1},
},
result: &IAM{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID"}, SetUpStarted: true, GlobalOrgID: "GlobalOrg"},
result: &IAM{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID"}, SetUpStarted: Step1, GlobalOrgID: "GlobalOrg"},
},
{
name: "append iamproject event",
args: args{
event: &es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: IAMProjectSet, ResourceOwner: "OrgID", Data: mockIamData(&IAM{IAMProjectID: "IamProject"})},
iam: &IAM{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID"}, SetUpStarted: true},
iam: &IAM{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID"}, SetUpStarted: Step1},
},
result: &IAM{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID"}, SetUpStarted: true, IAMProjectID: "IamProject"},
result: &IAM{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID"}, SetUpStarted: Step1, IAMProjectID: "IamProject"},
},
}
for _, tt := range tests {