feat: Policy (#79)

* policy added

* Make it executable

* Make it executable, corrections

* password age policy added

* password lockout policy added

* corrections

* policy added

* Make it executable

* Make it executable, corrections

* password age policy added

* password lockout policy added

* corrections

* fix(repository): remove second policy

* complaints corrected

* Init tests

* add some tests

* more tests added

* systemfefaults added

* default values load added

* check for default value added

* fixes

* fixed

* create policy if not exists

* eventstore tests added

Co-authored-by: adlerhurst <silvan.reusser@gmail.com>
This commit is contained in:
Michael Waeger
2020-05-14 11:48:57 +02:00
committed by GitHub
parent 767bc5ce6c
commit b9c938594c
46 changed files with 3529 additions and 851 deletions

View File

@@ -1,11 +1,15 @@
package systemdefaults
import "github.com/caos/zitadel/internal/crypto"
import (
"github.com/caos/zitadel/internal/crypto"
pol "github.com/caos/zitadel/internal/policy"
)
type SystemDefaults struct {
SecretGenerators SecretGenerators
UserVerificationKey *crypto.KeyConfig
Multifactors MultifactorConfig
DefaultPolicies DefaultPolicies
}
type SecretGenerators struct {
@@ -25,3 +29,9 @@ type OTPConfig struct {
Issuer string
VerificationKey *crypto.KeyConfig
}
type DefaultPolicies struct {
Age pol.PasswordAgePolicyDefault
Complexity pol.PasswordComplexityPolicyDefault
Lockout pol.PasswordLockoutPolicyDefault
}

View File

@@ -0,0 +1,45 @@
package eventsourcing
import (
"context"
"github.com/caos/zitadel/internal/api/auth"
pol_model "github.com/caos/zitadel/internal/policy/model"
pol_event "github.com/caos/zitadel/internal/policy/repository/eventsourcing"
)
type PolicyRepo struct {
PolicyEvents *pol_event.PolicyEventstore
//view *view.View
}
func (repo *PolicyRepo) CreatePasswordComplexityPolicy(ctx context.Context, policy *pol_model.PasswordComplexityPolicy) (*pol_model.PasswordComplexityPolicy, error) {
return repo.PolicyEvents.CreatePasswordComplexityPolicy(ctx, policy)
}
func (repo *PolicyRepo) GetPasswordComplexityPolicy(ctx context.Context) (*pol_model.PasswordComplexityPolicy, error) {
ctxData := auth.GetCtxData(ctx)
return repo.PolicyEvents.GetPasswordComplexityPolicy(ctx, ctxData.OrgID)
}
func (repo *PolicyRepo) UpdatePasswordComplexityPolicy(ctx context.Context, policy *pol_model.PasswordComplexityPolicy) (*pol_model.PasswordComplexityPolicy, error) {
return repo.PolicyEvents.UpdatePasswordComplexityPolicy(ctx, policy)
}
func (repo *PolicyRepo) CreatePasswordAgePolicy(ctx context.Context, policy *pol_model.PasswordAgePolicy) (*pol_model.PasswordAgePolicy, error) {
return repo.PolicyEvents.CreatePasswordAgePolicy(ctx, policy)
}
func (repo *PolicyRepo) GetPasswordAgePolicy(ctx context.Context) (*pol_model.PasswordAgePolicy, error) {
ctxData := auth.GetCtxData(ctx)
return repo.PolicyEvents.GetPasswordAgePolicy(ctx, ctxData.OrgID)
}
func (repo *PolicyRepo) UpdatePasswordAgePolicy(ctx context.Context, policy *pol_model.PasswordAgePolicy) (*pol_model.PasswordAgePolicy, error) {
return repo.PolicyEvents.UpdatePasswordAgePolicy(ctx, policy)
}
func (repo *PolicyRepo) CreatePasswordLockoutPolicy(ctx context.Context, policy *pol_model.PasswordLockoutPolicy) (*pol_model.PasswordLockoutPolicy, error) {
return repo.PolicyEvents.CreatePasswordLockoutPolicy(ctx, policy)
}
func (repo *PolicyRepo) GetPasswordLockoutPolicy(ctx context.Context) (*pol_model.PasswordLockoutPolicy, error) {
ctxData := auth.GetCtxData(ctx)
return repo.PolicyEvents.GetPasswordLockoutPolicy(ctx, ctxData.OrgID)
}
func (repo *PolicyRepo) UpdatePasswordLockoutPolicy(ctx context.Context, policy *pol_model.PasswordLockoutPolicy) (*pol_model.PasswordLockoutPolicy, error) {
return repo.PolicyEvents.UpdatePasswordLockoutPolicy(ctx, policy)
}

View File

@@ -12,6 +12,7 @@ import (
"github.com/caos/zitadel/internal/management/repository/eventsourcing/spooler"
mgmt_view "github.com/caos/zitadel/internal/management/repository/eventsourcing/view"
es_org "github.com/caos/zitadel/internal/org/repository/eventsourcing"
es_pol "github.com/caos/zitadel/internal/policy/repository/eventsourcing"
es_proj "github.com/caos/zitadel/internal/project/repository/eventsourcing"
es_usr "github.com/caos/zitadel/internal/user/repository/eventsourcing"
es_grant "github.com/caos/zitadel/internal/usergrant/repository/eventsourcing"
@@ -30,6 +31,7 @@ type EsRepository struct {
eventstore.ProjectRepo
eventstore.UserRepo
eventstore.UserGrantRepo
PolicyRepo
}
func Start(conf Config, systemDefaults sd.SystemDefaults) (*EsRepository, error) {
@@ -54,6 +56,13 @@ func Start(conf Config, systemDefaults sd.SystemDefaults) (*EsRepository, error)
if err != nil {
return nil, err
}
policy, err := es_pol.StartPolicy(es_pol.PolicyConfig{
Eventstore: es,
Cache: conf.Eventstore.Cache,
}, systemDefaults)
if err != nil {
return nil, err
}
user, err := es_usr.StartUser(es_usr.UserConfig{
Eventstore: es,
Cache: conf.Eventstore.Cache,
@@ -79,6 +88,7 @@ func Start(conf Config, systemDefaults sd.SystemDefaults) (*EsRepository, error)
ProjectRepo: eventstore.ProjectRepo{conf.SearchLimit, project, view},
UserRepo: eventstore.UserRepo{conf.SearchLimit, user, view},
UserGrantRepo: eventstore.UserGrantRepo{conf.SearchLimit, usergrant, view},
PolicyRepo: PolicyRepo{policy},
}, nil
}

View File

@@ -0,0 +1,19 @@
package repository
import (
"context"
"github.com/caos/zitadel/internal/policy/model"
)
type PolicyRepository interface {
CreatePasswordComplexityPolicy(ctx context.Context, policy *model.PasswordComplexityPolicy) (*model.PasswordComplexityPolicy, error)
GetPasswordComplexityPolicy(ctx context.Context) (*model.PasswordComplexityPolicy, error)
UpdatePasswordComplexityPolicy(ctx context.Context, policy *model.PasswordComplexityPolicy) (*model.PasswordComplexityPolicy, error)
CreatePasswordAgePolicy(ctx context.Context, policy *model.PasswordAgePolicy) (*model.PasswordAgePolicy, error)
GetPasswordAgePolicy(ctx context.Context) (*model.PasswordAgePolicy, error)
UpdatePasswordAgePolicy(ctx context.Context, policy *model.PasswordAgePolicy) (*model.PasswordAgePolicy, error)
CreatePasswordLockoutPolicy(ctx context.Context, policy *model.PasswordLockoutPolicy) (*model.PasswordLockoutPolicy, error)
GetPasswordLockoutPolicy(ctx context.Context) (*model.PasswordLockoutPolicy, error)
UpdatePasswordLockoutPolicy(ctx context.Context, policy *model.PasswordLockoutPolicy) (*model.PasswordLockoutPolicy, error)
}

View File

@@ -3,6 +3,7 @@ package repository
type Repository interface {
Health() error
ProjectRepository
PolicyRepository
OrgRepository
OrgMemberRepository
UserRepository

View File

@@ -0,0 +1,22 @@
package policy
type PasswordAgePolicyDefault struct {
Description string
MaxAgeDays uint64
ExpireWarnDays uint64
}
type PasswordComplexityPolicyDefault struct {
Description string
MinLength uint64
HasLowercase bool
HasUppercase bool
HasNumber bool
HasSymbol bool
}
type PasswordLockoutPolicyDefault struct {
Description string
MaxAttempts uint64
ShowLockOutFailures bool
}

View File

@@ -0,0 +1,16 @@
package model
import "github.com/caos/zitadel/internal/eventstore/models"
type PasswordAgePolicy struct {
models.ObjectRoot
Description string
State PolicyState
MaxAgeDays uint64
ExpireWarnDays uint64
}
func (p *PasswordAgePolicy) IsValid() bool {
return p.Description != ""
}

View File

@@ -0,0 +1,19 @@
package model
import "github.com/caos/zitadel/internal/eventstore/models"
type PasswordComplexityPolicy struct {
models.ObjectRoot
Description string
State PolicyState
MinLength uint64
HasLowercase bool
HasUppercase bool
HasNumber bool
HasSymbol bool
}
func (p *PasswordComplexityPolicy) IsValid() bool {
return p.Description != ""
}

View File

@@ -0,0 +1,16 @@
package model
import "github.com/caos/zitadel/internal/eventstore/models"
type PasswordLockoutPolicy struct {
models.ObjectRoot
Description string
State PolicyState
MaxAttempts uint64
ShowLockOutFailures bool
}
func (p *PasswordLockoutPolicy) IsValid() bool {
return p.Description != ""
}

View File

@@ -0,0 +1,30 @@
package model
import "github.com/caos/zitadel/internal/eventstore/models"
const (
// complexity
PasswordComplexityPolicyAggregate models.AggregateType = "policy.password.complexity"
PasswordComplexityPolicyAdded models.EventType = "policy.password.complexity.added"
PasswordComplexityPolicyChanged models.EventType = "policy.password.complexity.changed"
// age
PasswordAgePolicyAggregate models.AggregateType = "policy.password.age"
PasswordAgePolicyAdded models.EventType = "policy.password.age.added"
PasswordAgePolicyChanged models.EventType = "policy.password.age.changed"
// lockout
PasswordLockoutPolicyAggregate models.AggregateType = "policy.password.lockout"
PasswordLockoutPolicyAdded models.EventType = "policy.password.lockout.added"
PasswordLockoutPolicyChanged models.EventType = "policy.password.lockout.changed"
)
type PolicyState int32
const (
POLICYSTATE_ACTIVE PolicyState = iota
POLICYSTATE_INACTIVE
)

View File

@@ -0,0 +1,34 @@
package eventsourcing
import (
"github.com/caos/logging"
"github.com/caos/zitadel/internal/cache"
"github.com/caos/zitadel/internal/cache/config"
"github.com/caos/zitadel/internal/eventstore/models"
)
type PolicyCache struct {
policyCache cache.Cache
}
func StartCache(conf *config.CacheConfig) (*PolicyCache, error) {
policyCache, err := conf.Config.NewCache()
logging.Log("EVENT-vDneN").OnError(err).Panic("unable to create policy cache")
return &PolicyCache{policyCache: policyCache}, nil
}
func (c *PolicyCache) getPolicy(id string) (policy *PasswordComplexityPolicy) {
policy = &PasswordComplexityPolicy{ObjectRoot: models.ObjectRoot{AggregateID: id}}
if err := c.policyCache.Get(id, policy); err != nil {
logging.Log("EVENT-4eTZh").WithError(err).Debug("error in getting cache")
}
return policy
}
func (c *PolicyCache) cachePolicy(policy *PasswordComplexityPolicy) {
err := c.policyCache.Set(policy.AggregateID, policy)
if err != nil {
logging.Log("EVENT-ThnBb").WithError(err).Debug("error in setting policy cache")
}
}

View File

@@ -0,0 +1,20 @@
package eventsourcing
import (
"github.com/caos/logging"
)
func (c *PolicyCache) getAgePolicy(id string) (policy *PasswordAgePolicy) {
policy = new(PasswordAgePolicy)
if err := c.policyCache.Get(id, policy); err != nil {
logging.Log("EVENT-NqMFM").WithError(err).Debug("error in getting cache")
}
return policy
}
func (c *PolicyCache) cacheAgePolicy(policy *PasswordAgePolicy) {
err := c.policyCache.Set(policy.AggregateID, policy)
if err != nil {
logging.Log("EVENT-6vRvM").WithError(err).Debug("error in setting policy cache")
}
}

View File

@@ -0,0 +1,20 @@
package eventsourcing
import (
"github.com/caos/logging"
)
func (c *PolicyCache) getComplexityPolicy(id string) (policy *PasswordComplexityPolicy) {
policy = new(PasswordComplexityPolicy)
if err := c.policyCache.Get(id, policy); err != nil {
logging.Log("EVENT-Wgrph").WithError(err).Debug("error in getting cache")
}
return policy
}
func (c *PolicyCache) cacheComplexityPolicy(policy *PasswordComplexityPolicy) {
err := c.policyCache.Set(policy.AggregateID, policy)
if err != nil {
logging.Log("EVENT-ylGny").WithError(err).Debug("error in setting policy cache")
}
}

View File

@@ -0,0 +1,20 @@
package eventsourcing
import (
"github.com/caos/logging"
)
func (c *PolicyCache) getLockoutPolicy(id string) (policy *PasswordLockoutPolicy) {
policy = new(PasswordLockoutPolicy)
if err := c.policyCache.Get(id, policy); err != nil {
logging.Log("EVENT-Zoljf").WithError(err).Debug("error in getting cache")
}
return policy
}
func (c *PolicyCache) cacheLockoutPolicy(policy *PasswordLockoutPolicy) {
err := c.policyCache.Set(policy.AggregateID, policy)
if err != nil {
logging.Log("EVENT-6klAf").WithError(err).Debug("error in setting policy cache")
}
}

View File

@@ -0,0 +1,38 @@
package eventsourcing
import (
"github.com/caos/zitadel/internal/cache/config"
sd "github.com/caos/zitadel/internal/config/systemdefaults"
es_int "github.com/caos/zitadel/internal/eventstore"
"github.com/caos/zitadel/internal/policy"
"github.com/sony/sonyflake"
)
var idGenerator = sonyflake.NewSonyflake(sonyflake.Settings{})
type PolicyEventstore struct {
es_int.Eventstore
policyCache *PolicyCache
passwordAgePolicyDefault policy.PasswordAgePolicyDefault
passwordComplexityPolicyDefault policy.PasswordComplexityPolicyDefault
passwordLockoutPolicyDefault policy.PasswordLockoutPolicyDefault
}
type PolicyConfig struct {
es_int.Eventstore
Cache *config.CacheConfig
}
func StartPolicy(conf PolicyConfig, systemDefaults sd.SystemDefaults) (*PolicyEventstore, error) {
policyCache, err := StartCache(conf.Cache)
if err != nil {
return nil, err
}
return &PolicyEventstore{
Eventstore: conf.Eventstore,
policyCache: policyCache,
passwordAgePolicyDefault: systemDefaults.DefaultPolicies.Age,
passwordComplexityPolicyDefault: systemDefaults.DefaultPolicies.Complexity,
passwordLockoutPolicyDefault: systemDefaults.DefaultPolicies.Lockout,
}, nil
}

View File

@@ -0,0 +1,83 @@
package eventsourcing
import (
"context"
"strconv"
"github.com/caos/zitadel/internal/api/auth"
caos_errs "github.com/caos/zitadel/internal/errors"
es_sdk "github.com/caos/zitadel/internal/eventstore/sdk"
pol_model "github.com/caos/zitadel/internal/policy/model"
)
func (es *PolicyEventstore) GetPasswordAgePolicy(ctx context.Context, id string) (*pol_model.PasswordAgePolicy, error) {
policy := es.policyCache.getAgePolicy(id)
query := PasswordAgePolicyQuery(id, policy.Sequence)
err := es_sdk.Filter(ctx, es.FilterEvents, policy.AppendEvents, query)
if caos_errs.IsNotFound(err) && es.passwordAgePolicyDefault.Description != "" {
policy.Description = es.passwordAgePolicyDefault.Description
policy.MaxAgeDays = es.passwordAgePolicyDefault.MaxAgeDays
policy.ExpireWarnDays = es.passwordAgePolicyDefault.ExpireWarnDays
} else if err != nil {
return nil, err
}
es.policyCache.cacheAgePolicy(policy)
return PasswordAgePolicyToModel(policy), nil
}
func (es *PolicyEventstore) CreatePasswordAgePolicy(ctx context.Context, policy *pol_model.PasswordAgePolicy) (*pol_model.PasswordAgePolicy, error) {
if !policy.IsValid() {
return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-fbX5K", "Description is required")
}
ctxData := auth.GetCtxData(ctx)
existingPolicy, err := es.GetPasswordAgePolicy(ctx, ctxData.OrgID)
if err != nil && !caos_errs.IsNotFound(err) {
return nil, err
}
if existingPolicy != nil && existingPolicy.Sequence > 0 {
return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-yDJ5I", "Policy allready exists")
}
id, err := idGenerator.NextID()
if err != nil {
return nil, err
}
policy.AggregateID = strconv.FormatUint(id, 10)
repoPolicy := PasswordAgePolicyFromModel(policy)
createAggregate := PasswordAgePolicyCreateAggregate(es.AggregateCreator(), repoPolicy)
err = es_sdk.Push(ctx, es.PushAggregates, repoPolicy.AppendEvents, createAggregate)
if err != nil {
return nil, err
}
es.policyCache.cacheAgePolicy(repoPolicy)
return PasswordAgePolicyToModel(repoPolicy), nil
}
func (es *PolicyEventstore) UpdatePasswordAgePolicy(ctx context.Context, policy *pol_model.PasswordAgePolicy) (*pol_model.PasswordAgePolicy, error) {
if !policy.IsValid() {
return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-44jB3", "Description is required")
}
ctxData := auth.GetCtxData(ctx)
existingPolicy, err := es.GetPasswordAgePolicy(ctx, ctxData.OrgID)
if err != nil {
return nil, err
}
if existingPolicy.Sequence <= 0 {
return es.CreatePasswordAgePolicy(ctx, policy)
}
repoExisting := PasswordAgePolicyFromModel(existingPolicy)
repoNew := PasswordAgePolicyFromModel(policy)
updateAggregate := PasswordAgePolicyUpdateAggregate(es.AggregateCreator(), repoExisting, repoNew)
err = es_sdk.Push(ctx, es.PushAggregates, repoExisting.AppendEvents, updateAggregate)
if err != nil {
return nil, err
}
es.policyCache.cacheAgePolicy(repoExisting)
return PasswordAgePolicyToModel(repoExisting), nil
}

View File

@@ -0,0 +1,63 @@
package eventsourcing
import (
"encoding/json"
mock_cache "github.com/caos/zitadel/internal/cache/mock"
"github.com/caos/zitadel/internal/eventstore/mock"
es_models "github.com/caos/zitadel/internal/eventstore/models"
"github.com/caos/zitadel/internal/policy/model"
"github.com/golang/mock/gomock"
)
func GetMockedEventstoreAge(ctrl *gomock.Controller, mockEs *mock.MockEventstore) *PolicyEventstore {
return &PolicyEventstore{
Eventstore: mockEs,
policyCache: GetMockCacheAge(ctrl),
}
}
func GetMockCacheAge(ctrl *gomock.Controller) *PolicyCache {
mockCache := mock_cache.NewMockCache(ctrl)
mockCache.EXPECT().Get(gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
mockCache.EXPECT().Set(gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
return &PolicyCache{policyCache: mockCache}
}
func GetMockGetPasswordAgePolicyOK(ctrl *gomock.Controller) *PolicyEventstore {
data, _ := json.Marshal(model.PasswordAgePolicy{Description: "Name"})
events := []*es_models.Event{
&es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: model.PasswordAgePolicyAdded, Data: data},
}
mockEs := mock.NewMockEventstore(ctrl)
mockEs.EXPECT().FilterEvents(gomock.Any(), gomock.Any()).Return(events, nil)
return GetMockedEventstoreAge(ctrl, mockEs)
}
func GetMockGetPasswordAgePolicyNoEvents(ctrl *gomock.Controller) *PolicyEventstore {
events := []*es_models.Event{}
mockEs := mock.NewMockEventstore(ctrl)
mockEs.EXPECT().FilterEvents(gomock.Any(), gomock.Any()).Return(events, nil)
return GetMockedEventstoreAge(ctrl, mockEs)
}
func GetMockPasswordAgePolicy(ctrl *gomock.Controller) *PolicyEventstore {
data, _ := json.Marshal(model.PasswordAgePolicy{Description: "Name"})
events := []*es_models.Event{
&es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: model.PasswordAgePolicyAdded, Data: data},
}
mockEs := mock.NewMockEventstore(ctrl)
mockEs.EXPECT().FilterEvents(gomock.Any(), gomock.Any()).Return(events, nil)
mockEs.EXPECT().AggregateCreator().Return(es_models.NewAggregateCreator("TEST"))
mockEs.EXPECT().PushAggregates(gomock.Any(), gomock.Any()).Return(nil)
return GetMockedEventstoreAge(ctrl, mockEs)
}
func GetMockPasswordAgePolicyNoEvents(ctrl *gomock.Controller) *PolicyEventstore {
events := []*es_models.Event{}
mockEs := mock.NewMockEventstore(ctrl)
mockEs.EXPECT().FilterEvents(gomock.Any(), gomock.Any()).Return(events, nil)
mockEs.EXPECT().AggregateCreator().Return(es_models.NewAggregateCreator("TEST"))
mockEs.EXPECT().PushAggregates(gomock.Any(), gomock.Any()).Return(nil)
return GetMockedEventstoreAge(ctrl, mockEs)
}

View File

@@ -0,0 +1,192 @@
package eventsourcing
import (
"context"
"testing"
"github.com/caos/zitadel/internal/api/auth"
caos_errs "github.com/caos/zitadel/internal/errors"
es_models "github.com/caos/zitadel/internal/eventstore/models"
"github.com/caos/zitadel/internal/policy/model"
"github.com/golang/mock/gomock"
)
func TestGetPasswordAgePolicy(t *testing.T) {
ctrl := gomock.NewController(t)
type args struct {
es *PolicyEventstore
policy *model.PasswordAgePolicy
}
type res struct {
policy *model.PasswordAgePolicy
wantErr bool
errFunc func(err error) bool
}
tests := []struct {
name string
args args
res res
}{
{
name: "policy from events, ok",
args: args{
es: GetMockGetPasswordAgePolicyOK(ctrl),
policy: &model.PasswordAgePolicy{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 1}},
},
res: res{
policy: &model.PasswordAgePolicy{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 1}},
},
},
{
name: "policy from events, no events",
args: args{
es: GetMockGetPasswordAgePolicyNoEvents(ctrl),
policy: &model.PasswordAgePolicy{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 2}},
},
res: res{
wantErr: true,
errFunc: caos_errs.IsNotFound,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result, err := tt.args.es.GetPasswordAgePolicy(nil, tt.args.policy.AggregateID)
if !tt.res.wantErr && result.AggregateID != tt.res.policy.AggregateID {
t.Errorf("got wrong result name: expected: %v, actual: %v ", tt.res.policy.AggregateID, result.AggregateID)
}
if tt.res.wantErr && !tt.res.errFunc(err) {
t.Errorf("got wrong err: %v ", err)
}
})
}
}
func TestCreatePasswordAgePolicy(t *testing.T) {
ctrl := gomock.NewController(t)
type args struct {
es *PolicyEventstore
ctx context.Context
policy *model.PasswordAgePolicy
}
type res struct {
policy *model.PasswordAgePolicy
wantErr bool
errFunc func(err error) bool
}
tests := []struct {
name string
args args
res res
}{
{
name: "create policy, ok",
args: args{
es: GetMockPasswordAgePolicyNoEvents(ctrl),
ctx: auth.NewMockContext("orgID", "userID"),
policy: &model.PasswordAgePolicy{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID1", Sequence: 2}, Description: "Name"},
},
res: res{
policy: &model.PasswordAgePolicy{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID1", Sequence: 2}, Description: "Name"},
},
},
{
name: "create policy no name",
args: args{
es: GetMockPasswordAgePolicyNoEvents(ctrl),
ctx: auth.NewMockContext("orgID", "userID"),
policy: &model.PasswordAgePolicy{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 1}},
},
res: res{
wantErr: true,
errFunc: caos_errs.IsPreconditionFailed,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result, err := tt.args.es.CreatePasswordAgePolicy(tt.args.ctx, tt.args.policy)
if !tt.res.wantErr && result.AggregateID == "" {
t.Errorf("result has no id")
}
if !tt.res.wantErr && result.Description != tt.res.policy.Description {
t.Errorf("got wrong result name: expected: %v, actual: %v ", tt.res.policy.Description, result.Description)
}
if tt.res.wantErr && !tt.res.errFunc(err) {
t.Errorf("got wrong err: %v ", err)
}
})
}
}
func TestUpdatePasswordAgePolicy(t *testing.T) {
ctrl := gomock.NewController(t)
type args struct {
es *PolicyEventstore
ctx context.Context
new *model.PasswordAgePolicy
}
type res struct {
policy *model.PasswordAgePolicy
wantErr bool
errFunc func(err error) bool
}
tests := []struct {
name string
args args
res res
}{
{
name: "update policy, ok",
args: args{
es: GetMockPasswordAgePolicy(ctrl),
ctx: auth.NewMockContext("orgID", "userID"),
new: &model.PasswordAgePolicy{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 1}, Description: "NameNew"},
},
res: res{
policy: &model.PasswordAgePolicy{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 1}, Description: "NameNew"},
},
},
{
name: "update policy no name",
args: args{
es: GetMockPasswordAgePolicy(ctrl),
ctx: auth.NewMockContext("orgID", "userID"),
new: &model.PasswordAgePolicy{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 1}, Description: ""},
},
res: res{
wantErr: true,
errFunc: caos_errs.IsPreconditionFailed,
},
},
{
name: "existing policy not found",
args: args{
es: GetMockPasswordAgePolicyNoEvents(ctrl),
ctx: auth.NewMockContext("orgID", "userID"),
new: &model.PasswordAgePolicy{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 1}, Description: "NameNew"},
},
res: res{
wantErr: true,
errFunc: caos_errs.IsNotFound,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result, err := tt.args.es.UpdatePasswordAgePolicy(tt.args.ctx, tt.args.new)
if !tt.res.wantErr && result.AggregateID == "" {
t.Errorf("result has no id")
}
if !tt.res.wantErr && result.Description != tt.res.policy.Description {
t.Errorf("got wrong result name: expected: %v, actual: %v ", tt.res.policy.Description, result.Description)
}
if tt.res.wantErr && !tt.res.errFunc(err) {
t.Errorf("got wrong err: %v ", err)
}
})
}
}

View File

@@ -0,0 +1,86 @@
package eventsourcing
import (
"context"
"strconv"
"github.com/caos/zitadel/internal/api/auth"
caos_errs "github.com/caos/zitadel/internal/errors"
es_sdk "github.com/caos/zitadel/internal/eventstore/sdk"
pol_model "github.com/caos/zitadel/internal/policy/model"
)
func (es *PolicyEventstore) GetPasswordComplexityPolicy(ctx context.Context, id string) (*pol_model.PasswordComplexityPolicy, error) {
policy := es.policyCache.getComplexityPolicy(id)
query := PasswordComplexityPolicyQuery(id, policy.Sequence)
err := es_sdk.Filter(ctx, es.FilterEvents, policy.AppendEvents, query)
if caos_errs.IsNotFound(err) && es.passwordComplexityPolicyDefault.Description != "" {
policy.Description = es.passwordComplexityPolicyDefault.Description
policy.MinLength = es.passwordComplexityPolicyDefault.MinLength
policy.HasLowercase = es.passwordComplexityPolicyDefault.HasLowercase
policy.HasUppercase = es.passwordComplexityPolicyDefault.HasUppercase
policy.HasNumber = es.passwordComplexityPolicyDefault.HasNumber
policy.HasSymbol = es.passwordComplexityPolicyDefault.HasSymbol
} else if err != nil {
return nil, err
}
es.policyCache.cacheComplexityPolicy(policy)
return PasswordComplexityPolicyToModel(policy), nil
}
func (es *PolicyEventstore) CreatePasswordComplexityPolicy(ctx context.Context, policy *pol_model.PasswordComplexityPolicy) (*pol_model.PasswordComplexityPolicy, error) {
if !policy.IsValid() {
return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-9dk45", "Description is required")
}
ctxData := auth.GetCtxData(ctx)
existingPolicy, err := es.GetPasswordComplexityPolicy(ctx, ctxData.OrgID)
if err != nil && !caos_errs.IsNotFound(err) {
return nil, err
}
if existingPolicy != nil && existingPolicy.Sequence > 0 {
return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-yDJ5I", "Policy allready exists")
}
id, err := idGenerator.NextID()
if err != nil {
return nil, err
}
policy.AggregateID = strconv.FormatUint(id, 10)
repoPolicy := PasswordComplexityPolicyFromModel(policy)
createAggregate := PasswordComplexityPolicyCreateAggregate(es.AggregateCreator(), repoPolicy)
err = es_sdk.Push(ctx, es.PushAggregates, repoPolicy.AppendEvents, createAggregate)
if err != nil {
return nil, err
}
es.policyCache.cacheComplexityPolicy(repoPolicy)
return PasswordComplexityPolicyToModel(repoPolicy), nil
}
func (es *PolicyEventstore) UpdatePasswordComplexityPolicy(ctx context.Context, policy *pol_model.PasswordComplexityPolicy) (*pol_model.PasswordComplexityPolicy, error) {
if !policy.IsValid() {
return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-9dk45", "Description is required")
}
ctxData := auth.GetCtxData(ctx)
existingPolicy, err := es.GetPasswordComplexityPolicy(ctx, ctxData.OrgID)
if err != nil {
return nil, err
}
if existingPolicy.Sequence <= 0 {
return es.CreatePasswordComplexityPolicy(ctx, policy)
}
repoExisting := PasswordComplexityPolicyFromModel(existingPolicy)
repoNew := PasswordComplexityPolicyFromModel(policy)
updateAggregate := PasswordComplexityPolicyUpdateAggregate(es.AggregateCreator(), repoExisting, repoNew)
err = es_sdk.Push(ctx, es.PushAggregates, repoExisting.AppendEvents, updateAggregate)
if err != nil {
return nil, err
}
es.policyCache.cacheComplexityPolicy(repoExisting)
return PasswordComplexityPolicyToModel(repoExisting), nil
}

View File

@@ -0,0 +1,63 @@
package eventsourcing
import (
"encoding/json"
mock_cache "github.com/caos/zitadel/internal/cache/mock"
"github.com/caos/zitadel/internal/eventstore/mock"
es_models "github.com/caos/zitadel/internal/eventstore/models"
"github.com/caos/zitadel/internal/policy/model"
"github.com/golang/mock/gomock"
)
func GetMockedEventstoreComplexity(ctrl *gomock.Controller, mockEs *mock.MockEventstore) *PolicyEventstore {
return &PolicyEventstore{
Eventstore: mockEs,
policyCache: GetMockCacheComplexity(ctrl),
}
}
func GetMockCacheComplexity(ctrl *gomock.Controller) *PolicyCache {
mockCache := mock_cache.NewMockCache(ctrl)
mockCache.EXPECT().Get(gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
mockCache.EXPECT().Set(gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
return &PolicyCache{policyCache: mockCache}
}
func GetMockGetPasswordComplexityPolicyOK(ctrl *gomock.Controller) *PolicyEventstore {
data, _ := json.Marshal(model.PasswordComplexityPolicy{Description: "Name"})
events := []*es_models.Event{
&es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: model.PasswordComplexityPolicyAdded, Data: data},
}
mockEs := mock.NewMockEventstore(ctrl)
mockEs.EXPECT().FilterEvents(gomock.Any(), gomock.Any()).Return(events, nil)
return GetMockedEventstoreComplexity(ctrl, mockEs)
}
func GetMockGetPasswordComplexityPolicyNoEvents(ctrl *gomock.Controller) *PolicyEventstore {
events := []*es_models.Event{}
mockEs := mock.NewMockEventstore(ctrl)
mockEs.EXPECT().FilterEvents(gomock.Any(), gomock.Any()).Return(events, nil)
return GetMockedEventstoreComplexity(ctrl, mockEs)
}
func GetMockPasswordComplexityPolicy(ctrl *gomock.Controller) *PolicyEventstore {
data, _ := json.Marshal(model.PasswordComplexityPolicy{Description: "Name"})
events := []*es_models.Event{
&es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: model.PasswordComplexityPolicyAdded, Data: data},
}
mockEs := mock.NewMockEventstore(ctrl)
mockEs.EXPECT().FilterEvents(gomock.Any(), gomock.Any()).Return(events, nil)
mockEs.EXPECT().AggregateCreator().Return(es_models.NewAggregateCreator("TEST"))
mockEs.EXPECT().PushAggregates(gomock.Any(), gomock.Any()).Return(nil)
return GetMockedEventstoreComplexity(ctrl, mockEs)
}
func GetMockPasswordComplexityPolicyNoEvents(ctrl *gomock.Controller) *PolicyEventstore {
events := []*es_models.Event{}
mockEs := mock.NewMockEventstore(ctrl)
mockEs.EXPECT().FilterEvents(gomock.Any(), gomock.Any()).Return(events, nil)
mockEs.EXPECT().AggregateCreator().Return(es_models.NewAggregateCreator("TEST"))
mockEs.EXPECT().PushAggregates(gomock.Any(), gomock.Any()).Return(nil)
return GetMockedEventstoreComplexity(ctrl, mockEs)
}

View File

@@ -0,0 +1,192 @@
package eventsourcing
import (
"context"
"testing"
"github.com/caos/zitadel/internal/api/auth"
caos_errs "github.com/caos/zitadel/internal/errors"
es_models "github.com/caos/zitadel/internal/eventstore/models"
"github.com/caos/zitadel/internal/policy/model"
"github.com/golang/mock/gomock"
)
func TestGetPasswordComplexityPolicy(t *testing.T) {
ctrl := gomock.NewController(t)
type args struct {
es *PolicyEventstore
policy *model.PasswordComplexityPolicy
}
type res struct {
policy *model.PasswordComplexityPolicy
wantErr bool
errFunc func(err error) bool
}
tests := []struct {
name string
args args
res res
}{
{
name: "policy from events, ok",
args: args{
es: GetMockGetPasswordComplexityPolicyOK(ctrl),
policy: &model.PasswordComplexityPolicy{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 1}},
},
res: res{
policy: &model.PasswordComplexityPolicy{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 1}},
},
},
{
name: "policy from events, no events",
args: args{
es: GetMockGetPasswordComplexityPolicyNoEvents(ctrl),
policy: &model.PasswordComplexityPolicy{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 2}},
},
res: res{
wantErr: true,
errFunc: caos_errs.IsNotFound,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result, err := tt.args.es.GetPasswordComplexityPolicy(nil, tt.args.policy.AggregateID)
if !tt.res.wantErr && result.AggregateID != tt.res.policy.AggregateID {
t.Errorf("got wrong result name: expected: %v, actual: %v ", tt.res.policy.AggregateID, result.AggregateID)
}
if tt.res.wantErr && !tt.res.errFunc(err) {
t.Errorf("got wrong err: %v ", err)
}
})
}
}
func TestCreatePasswordComplexityPolicy(t *testing.T) {
ctrl := gomock.NewController(t)
type args struct {
es *PolicyEventstore
ctx context.Context
policy *model.PasswordComplexityPolicy
}
type res struct {
policy *model.PasswordComplexityPolicy
wantErr bool
errFunc func(err error) bool
}
tests := []struct {
name string
args args
res res
}{
{
name: "create policy, ok",
args: args{
es: GetMockPasswordComplexityPolicyNoEvents(ctrl),
ctx: auth.NewMockContext("orgID", "userID"),
policy: &model.PasswordComplexityPolicy{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID1", Sequence: 2}, Description: "Name"},
},
res: res{
policy: &model.PasswordComplexityPolicy{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID1", Sequence: 2}, Description: "Name"},
},
},
{
name: "create policy no name",
args: args{
es: GetMockPasswordComplexityPolicyNoEvents(ctrl),
ctx: auth.NewMockContext("orgID", "userID"),
policy: &model.PasswordComplexityPolicy{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 1}},
},
res: res{
wantErr: true,
errFunc: caos_errs.IsPreconditionFailed,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result, err := tt.args.es.CreatePasswordComplexityPolicy(tt.args.ctx, tt.args.policy)
if !tt.res.wantErr && result.AggregateID == "" {
t.Errorf("result has no id")
}
if !tt.res.wantErr && result.Description != tt.res.policy.Description {
t.Errorf("got wrong result name: expected: %v, actual: %v ", tt.res.policy.Description, result.Description)
}
if tt.res.wantErr && !tt.res.errFunc(err) {
t.Errorf("got wrong err: %v ", err)
}
})
}
}
func TestUpdatePasswordComplexityPolicy(t *testing.T) {
ctrl := gomock.NewController(t)
type args struct {
es *PolicyEventstore
ctx context.Context
new *model.PasswordComplexityPolicy
}
type res struct {
policy *model.PasswordComplexityPolicy
wantErr bool
errFunc func(err error) bool
}
tests := []struct {
name string
args args
res res
}{
{
name: "update policy, ok",
args: args{
es: GetMockPasswordComplexityPolicy(ctrl),
ctx: auth.NewMockContext("orgID", "userID"),
new: &model.PasswordComplexityPolicy{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 1}, Description: "NameNew"},
},
res: res{
policy: &model.PasswordComplexityPolicy{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 1}, Description: "NameNew"},
},
},
{
name: "update policy no name",
args: args{
es: GetMockPasswordComplexityPolicy(ctrl),
ctx: auth.NewMockContext("orgID", "userID"),
new: &model.PasswordComplexityPolicy{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 1}, Description: ""},
},
res: res{
wantErr: true,
errFunc: caos_errs.IsPreconditionFailed,
},
},
{
name: "existing policy not found",
args: args{
es: GetMockPasswordComplexityPolicyNoEvents(ctrl),
ctx: auth.NewMockContext("orgID", "userID"),
new: &model.PasswordComplexityPolicy{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 1}, Description: "NameNew"},
},
res: res{
wantErr: true,
errFunc: caos_errs.IsNotFound,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result, err := tt.args.es.UpdatePasswordComplexityPolicy(tt.args.ctx, tt.args.new)
if !tt.res.wantErr && result.AggregateID == "" {
t.Errorf("result has no id")
}
if !tt.res.wantErr && result.Description != tt.res.policy.Description {
t.Errorf("got wrong result name: expected: %v, actual: %v ", tt.res.policy.Description, result.Description)
}
if tt.res.wantErr && !tt.res.errFunc(err) {
t.Errorf("got wrong err: %v ", err)
}
})
}
}

View File

@@ -0,0 +1,83 @@
package eventsourcing
import (
"context"
"strconv"
"github.com/caos/zitadel/internal/api/auth"
caos_errs "github.com/caos/zitadel/internal/errors"
es_sdk "github.com/caos/zitadel/internal/eventstore/sdk"
pol_model "github.com/caos/zitadel/internal/policy/model"
)
func (es *PolicyEventstore) GetPasswordLockoutPolicy(ctx context.Context, id string) (*pol_model.PasswordLockoutPolicy, error) {
policy := es.policyCache.getLockoutPolicy(id)
query := PasswordLockoutPolicyQuery(id, policy.Sequence)
err := es_sdk.Filter(ctx, es.FilterEvents, policy.AppendEvents, query)
if caos_errs.IsNotFound(err) && es.passwordLockoutPolicyDefault.Description != "" {
policy.Description = es.passwordLockoutPolicyDefault.Description
policy.MaxAttempts = es.passwordLockoutPolicyDefault.MaxAttempts
policy.ShowLockOutFailures = es.passwordLockoutPolicyDefault.ShowLockOutFailures
} else if err != nil {
return nil, err
}
es.policyCache.cacheLockoutPolicy(policy)
return PasswordLockoutPolicyToModel(policy), nil
}
func (es *PolicyEventstore) CreatePasswordLockoutPolicy(ctx context.Context, policy *pol_model.PasswordLockoutPolicy) (*pol_model.PasswordLockoutPolicy, error) {
if !policy.IsValid() {
return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-9dk45", "Description is required")
}
ctxData := auth.GetCtxData(ctx)
existingPolicy, err := es.GetPasswordLockoutPolicy(ctx, ctxData.OrgID)
if err != nil && !caos_errs.IsNotFound(err) {
return nil, err
}
if existingPolicy != nil && existingPolicy.Sequence > 0 {
return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-yDJ5I", "Policy allready exists")
}
id, err := idGenerator.NextID()
if err != nil {
return nil, err
}
policy.AggregateID = strconv.FormatUint(id, 10)
repoPolicy := PasswordLockoutPolicyFromModel(policy)
createAggregate := PasswordLockoutPolicyCreateAggregate(es.AggregateCreator(), repoPolicy)
err = es_sdk.Push(ctx, es.PushAggregates, repoPolicy.AppendEvents, createAggregate)
if err != nil {
return nil, err
}
es.policyCache.cacheLockoutPolicy(repoPolicy)
return PasswordLockoutPolicyToModel(repoPolicy), nil
}
func (es *PolicyEventstore) UpdatePasswordLockoutPolicy(ctx context.Context, policy *pol_model.PasswordLockoutPolicy) (*pol_model.PasswordLockoutPolicy, error) {
if !policy.IsValid() {
return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-9dk45", "Description is required")
}
ctxData := auth.GetCtxData(ctx)
existingPolicy, err := es.GetPasswordLockoutPolicy(ctx, ctxData.OrgID)
if err != nil {
return nil, err
}
if existingPolicy.Sequence <= 0 {
return es.CreatePasswordLockoutPolicy(ctx, policy)
}
repoExisting := PasswordLockoutPolicyFromModel(existingPolicy)
repoNew := PasswordLockoutPolicyFromModel(policy)
updateAggregate := PasswordLockoutPolicyUpdateAggregate(es.AggregateCreator(), repoExisting, repoNew)
err = es_sdk.Push(ctx, es.PushAggregates, repoExisting.AppendEvents, updateAggregate)
if err != nil {
return nil, err
}
es.policyCache.cacheLockoutPolicy(repoExisting)
return PasswordLockoutPolicyToModel(repoExisting), nil
}

View File

@@ -0,0 +1,63 @@
package eventsourcing
import (
"encoding/json"
mock_cache "github.com/caos/zitadel/internal/cache/mock"
"github.com/caos/zitadel/internal/eventstore/mock"
es_models "github.com/caos/zitadel/internal/eventstore/models"
"github.com/caos/zitadel/internal/policy/model"
"github.com/golang/mock/gomock"
)
func GetMockedEventstoreLockout(ctrl *gomock.Controller, mockEs *mock.MockEventstore) *PolicyEventstore {
return &PolicyEventstore{
Eventstore: mockEs,
policyCache: GetMockCacheLockout(ctrl),
}
}
func GetMockCacheLockout(ctrl *gomock.Controller) *PolicyCache {
mockCache := mock_cache.NewMockCache(ctrl)
mockCache.EXPECT().Get(gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
mockCache.EXPECT().Set(gomock.Any(), gomock.Any()).Return(nil).AnyTimes()
return &PolicyCache{policyCache: mockCache}
}
func GetMockGetPasswordLockoutPolicyOK(ctrl *gomock.Controller) *PolicyEventstore {
data, _ := json.Marshal(model.PasswordLockoutPolicy{Description: "Name"})
events := []*es_models.Event{
&es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: model.PasswordLockoutPolicyAdded, Data: data},
}
mockEs := mock.NewMockEventstore(ctrl)
mockEs.EXPECT().FilterEvents(gomock.Any(), gomock.Any()).Return(events, nil)
return GetMockedEventstoreLockout(ctrl, mockEs)
}
func GetMockGetPasswordLockoutPolicyNoEvents(ctrl *gomock.Controller) *PolicyEventstore {
events := []*es_models.Event{}
mockEs := mock.NewMockEventstore(ctrl)
mockEs.EXPECT().FilterEvents(gomock.Any(), gomock.Any()).Return(events, nil)
return GetMockedEventstoreLockout(ctrl, mockEs)
}
func GetMockPasswordLockoutPolicy(ctrl *gomock.Controller) *PolicyEventstore {
data, _ := json.Marshal(model.PasswordLockoutPolicy{Description: "Name"})
events := []*es_models.Event{
&es_models.Event{AggregateID: "AggregateID", Sequence: 1, Type: model.PasswordLockoutPolicyAdded, Data: data},
}
mockEs := mock.NewMockEventstore(ctrl)
mockEs.EXPECT().FilterEvents(gomock.Any(), gomock.Any()).Return(events, nil)
mockEs.EXPECT().AggregateCreator().Return(es_models.NewAggregateCreator("TEST"))
mockEs.EXPECT().PushAggregates(gomock.Any(), gomock.Any()).Return(nil)
return GetMockedEventstoreLockout(ctrl, mockEs)
}
func GetMockPasswordLockoutPolicyNoEvents(ctrl *gomock.Controller) *PolicyEventstore {
events := []*es_models.Event{}
mockEs := mock.NewMockEventstore(ctrl)
mockEs.EXPECT().FilterEvents(gomock.Any(), gomock.Any()).Return(events, nil)
mockEs.EXPECT().AggregateCreator().Return(es_models.NewAggregateCreator("TEST"))
mockEs.EXPECT().PushAggregates(gomock.Any(), gomock.Any()).Return(nil)
return GetMockedEventstoreLockout(ctrl, mockEs)
}

View File

@@ -0,0 +1,192 @@
package eventsourcing
import (
"context"
"testing"
"github.com/caos/zitadel/internal/api/auth"
caos_errs "github.com/caos/zitadel/internal/errors"
es_models "github.com/caos/zitadel/internal/eventstore/models"
"github.com/caos/zitadel/internal/policy/model"
"github.com/golang/mock/gomock"
)
func TestGetPasswordLockoutPolicy(t *testing.T) {
ctrl := gomock.NewController(t)
type args struct {
es *PolicyEventstore
policy *model.PasswordLockoutPolicy
}
type res struct {
policy *model.PasswordLockoutPolicy
wantErr bool
errFunc func(err error) bool
}
tests := []struct {
name string
args args
res res
}{
{
name: "policy from events, ok",
args: args{
es: GetMockGetPasswordLockoutPolicyOK(ctrl),
policy: &model.PasswordLockoutPolicy{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 1}},
},
res: res{
policy: &model.PasswordLockoutPolicy{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 1}},
},
},
{
name: "policy from events, no events",
args: args{
es: GetMockGetPasswordLockoutPolicyNoEvents(ctrl),
policy: &model.PasswordLockoutPolicy{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 2}},
},
res: res{
wantErr: true,
errFunc: caos_errs.IsNotFound,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result, err := tt.args.es.GetPasswordLockoutPolicy(nil, tt.args.policy.AggregateID)
if !tt.res.wantErr && result.AggregateID != tt.res.policy.AggregateID {
t.Errorf("got wrong result name: expected: %v, actual: %v ", tt.res.policy.AggregateID, result.AggregateID)
}
if tt.res.wantErr && !tt.res.errFunc(err) {
t.Errorf("got wrong err: %v ", err)
}
})
}
}
func TestCreatePasswordLockoutPolicy(t *testing.T) {
ctrl := gomock.NewController(t)
type args struct {
es *PolicyEventstore
ctx context.Context
policy *model.PasswordLockoutPolicy
}
type res struct {
policy *model.PasswordLockoutPolicy
wantErr bool
errFunc func(err error) bool
}
tests := []struct {
name string
args args
res res
}{
{
name: "create policy, ok",
args: args{
es: GetMockPasswordLockoutPolicyNoEvents(ctrl),
ctx: auth.NewMockContext("orgID", "userID"),
policy: &model.PasswordLockoutPolicy{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID1", Sequence: 2}, Description: "Name"},
},
res: res{
policy: &model.PasswordLockoutPolicy{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID1", Sequence: 2}, Description: "Name"},
},
},
{
name: "create policy no name",
args: args{
es: GetMockPasswordLockoutPolicyNoEvents(ctrl),
ctx: auth.NewMockContext("orgID", "userID"),
policy: &model.PasswordLockoutPolicy{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 1}},
},
res: res{
wantErr: true,
errFunc: caos_errs.IsPreconditionFailed,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result, err := tt.args.es.CreatePasswordLockoutPolicy(tt.args.ctx, tt.args.policy)
if !tt.res.wantErr && result.AggregateID == "" {
t.Errorf("result has no id")
}
if !tt.res.wantErr && result.Description != tt.res.policy.Description {
t.Errorf("got wrong result name: expected: %v, actual: %v ", tt.res.policy.Description, result.Description)
}
if tt.res.wantErr && !tt.res.errFunc(err) {
t.Errorf("got wrong err: %v ", err)
}
})
}
}
func TestUpdatePasswordLockoutPolicy(t *testing.T) {
ctrl := gomock.NewController(t)
type args struct {
es *PolicyEventstore
ctx context.Context
new *model.PasswordLockoutPolicy
}
type res struct {
policy *model.PasswordLockoutPolicy
wantErr bool
errFunc func(err error) bool
}
tests := []struct {
name string
args args
res res
}{
{
name: "update policy, ok",
args: args{
es: GetMockPasswordLockoutPolicy(ctrl),
ctx: auth.NewMockContext("orgID", "userID"),
new: &model.PasswordLockoutPolicy{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 1}, Description: "NameNew"},
},
res: res{
policy: &model.PasswordLockoutPolicy{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 1}, Description: "NameNew"},
},
},
{
name: "update policy no name",
args: args{
es: GetMockPasswordLockoutPolicy(ctrl),
ctx: auth.NewMockContext("orgID", "userID"),
new: &model.PasswordLockoutPolicy{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 1}, Description: ""},
},
res: res{
wantErr: true,
errFunc: caos_errs.IsPreconditionFailed,
},
},
{
name: "existing policy not found",
args: args{
es: GetMockPasswordLockoutPolicyNoEvents(ctrl),
ctx: auth.NewMockContext("orgID", "userID"),
new: &model.PasswordLockoutPolicy{ObjectRoot: es_models.ObjectRoot{AggregateID: "AggregateID", Sequence: 1}, Description: "NameNew"},
},
res: res{
wantErr: true,
errFunc: caos_errs.IsNotFound,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result, err := tt.args.es.UpdatePasswordLockoutPolicy(tt.args.ctx, tt.args.new)
if !tt.res.wantErr && result.AggregateID == "" {
t.Errorf("result has no id")
}
if !tt.res.wantErr && result.Description != tt.res.policy.Description {
t.Errorf("got wrong result name: expected: %v, actual: %v ", tt.res.policy.Description, result.Description)
}
if tt.res.wantErr && !tt.res.errFunc(err) {
t.Errorf("got wrong err: %v ", err)
}
})
}
}

View File

@@ -0,0 +1,80 @@
package eventsourcing
import (
"encoding/json"
"github.com/caos/logging"
"github.com/caos/zitadel/internal/eventstore/models"
es_models "github.com/caos/zitadel/internal/eventstore/models"
"github.com/caos/zitadel/internal/policy/model"
)
const (
policyAgeVersion = "v1"
)
type PasswordAgePolicy struct {
models.ObjectRoot
Description string `json:"description,omitempty"`
State int32 `json:"-"`
MaxAgeDays uint64 `json:"maxAgeDays"`
ExpireWarnDays uint64 `json:"expireWarnDays"`
}
func (p *PasswordAgePolicy) AgeChanges(changed *PasswordAgePolicy) map[string]interface{} {
changes := make(map[string]interface{}, 1)
if changed.Description != "" && p.Description != changed.Description {
changes["description"] = changed.Description
}
if p.MaxAgeDays != changed.MaxAgeDays {
changes["maxAgeDays"] = changed.MaxAgeDays
}
if p.ExpireWarnDays != changed.ExpireWarnDays {
changes["expireWarnDays"] = changed.ExpireWarnDays
}
return changes
}
func PasswordAgePolicyFromModel(policy *model.PasswordAgePolicy) *PasswordAgePolicy {
return &PasswordAgePolicy{
ObjectRoot: policy.ObjectRoot,
Description: policy.Description,
State: int32(policy.State),
MaxAgeDays: policy.MaxAgeDays,
ExpireWarnDays: policy.ExpireWarnDays,
}
}
func PasswordAgePolicyToModel(policy *PasswordAgePolicy) *model.PasswordAgePolicy {
return &model.PasswordAgePolicy{
ObjectRoot: policy.ObjectRoot,
Description: policy.Description,
State: model.PolicyState(policy.State),
MaxAgeDays: policy.MaxAgeDays,
ExpireWarnDays: policy.ExpireWarnDays,
}
}
func (p *PasswordAgePolicy) AppendEvents(events ...*es_models.Event) error {
for _, event := range events {
if err := p.AppendEvent(event); err != nil {
return err
}
}
return nil
}
func (p *PasswordAgePolicy) AppendEvent(event *es_models.Event) error {
p.ObjectRoot.AppendEvent(event)
switch event.Type {
case model.PasswordAgePolicyAdded, model.PasswordAgePolicyChanged:
if err := json.Unmarshal(event.Data, p); err != nil {
logging.Log("EVEN-idl93").WithError(err).Error("could not unmarshal event data")
return err
}
return nil
}
return nil
}

View File

@@ -0,0 +1,98 @@
package eventsourcing
import (
"encoding/json"
"github.com/caos/logging"
"github.com/caos/zitadel/internal/eventstore/models"
es_models "github.com/caos/zitadel/internal/eventstore/models"
"github.com/caos/zitadel/internal/policy/model"
)
const (
policyComplexityVersion = "v1"
)
type PasswordComplexityPolicy struct {
models.ObjectRoot
Description string `json:"description,omitempty"`
State int32 `json:"-"`
MinLength uint64 `json:"minLength"`
HasLowercase bool `json:"hasLowercase"`
HasUppercase bool `json:"hasUppercase"`
HasNumber bool `json:"hasNumber"`
HasSymbol bool `json:"hasSymbol"`
}
func (p *PasswordComplexityPolicy) ComplexityChanges(changed *PasswordComplexityPolicy) map[string]interface{} {
changes := make(map[string]interface{}, 1)
if changed.Description != "" && p.Description != changed.Description {
changes["description"] = changed.Description
}
if p.MinLength != changed.MinLength {
changes["minLength"] = changed.MinLength
}
if p.HasLowercase != changed.HasLowercase {
changes["hasLowercase"] = changed.HasLowercase
}
if p.HasUppercase != changed.HasUppercase {
changes["hasUppercase"] = changed.HasUppercase
}
if p.HasNumber != changed.HasNumber {
changes["hasNumber"] = changed.HasNumber
}
if p.HasSymbol != changed.HasSymbol {
changes["hasSymbol"] = changed.HasSymbol
}
return changes
}
func PasswordComplexityPolicyFromModel(policy *model.PasswordComplexityPolicy) *PasswordComplexityPolicy {
return &PasswordComplexityPolicy{
ObjectRoot: policy.ObjectRoot,
Description: policy.Description,
State: int32(policy.State),
MinLength: policy.MinLength,
HasLowercase: policy.HasLowercase,
HasUppercase: policy.HasUppercase,
HasNumber: policy.HasNumber,
HasSymbol: policy.HasSymbol,
}
}
func PasswordComplexityPolicyToModel(policy *PasswordComplexityPolicy) *model.PasswordComplexityPolicy {
return &model.PasswordComplexityPolicy{
ObjectRoot: policy.ObjectRoot,
Description: policy.Description,
State: model.PolicyState(policy.State),
MinLength: policy.MinLength,
HasLowercase: policy.HasLowercase,
HasUppercase: policy.HasUppercase,
HasNumber: policy.HasNumber,
HasSymbol: policy.HasSymbol,
}
}
func (p *PasswordComplexityPolicy) AppendEvents(events ...*es_models.Event) error {
for _, event := range events {
if err := p.AppendEvent(event); err != nil {
return err
}
}
return nil
}
func (p *PasswordComplexityPolicy) AppendEvent(event *es_models.Event) error {
p.ObjectRoot.AppendEvent(event)
switch event.Type {
case model.PasswordComplexityPolicyAdded, model.PasswordComplexityPolicyChanged:
if err := json.Unmarshal(event.Data, p); err != nil {
logging.Log("EVEN-idl93").WithError(err).Error("could not unmarshal event data")
return err
}
return nil
}
return nil
}

View File

@@ -0,0 +1,80 @@
package eventsourcing
import (
"encoding/json"
"github.com/caos/logging"
"github.com/caos/zitadel/internal/eventstore/models"
es_models "github.com/caos/zitadel/internal/eventstore/models"
"github.com/caos/zitadel/internal/policy/model"
)
const (
policyLockoutVersion = "v1"
)
type PasswordLockoutPolicy struct {
models.ObjectRoot
Description string `json:"description,omitempty"`
State int32 `json:"-"`
MaxAttempts uint64 `json:"maxAttempts"`
ShowLockOutFailures bool `json:"showLockOutFailures"`
}
func (p *PasswordLockoutPolicy) LockoutChanges(changed *PasswordLockoutPolicy) map[string]interface{} {
changes := make(map[string]interface{}, 1)
if changed.Description != "" && p.Description != changed.Description {
changes["description"] = changed.Description
}
if p.MaxAttempts != changed.MaxAttempts {
changes["maxAttempts"] = changed.MaxAttempts
}
if p.ShowLockOutFailures != changed.ShowLockOutFailures {
changes["showLockOutFailures"] = changed.ShowLockOutFailures
}
return changes
}
func PasswordLockoutPolicyFromModel(policy *model.PasswordLockoutPolicy) *PasswordLockoutPolicy {
return &PasswordLockoutPolicy{
ObjectRoot: policy.ObjectRoot,
Description: policy.Description,
State: int32(policy.State),
MaxAttempts: policy.MaxAttempts,
ShowLockOutFailures: policy.ShowLockOutFailures,
}
}
func PasswordLockoutPolicyToModel(policy *PasswordLockoutPolicy) *model.PasswordLockoutPolicy {
return &model.PasswordLockoutPolicy{
ObjectRoot: policy.ObjectRoot,
Description: policy.Description,
State: model.PolicyState(policy.State),
MaxAttempts: policy.MaxAttempts,
ShowLockOutFailures: policy.ShowLockOutFailures,
}
}
func (p *PasswordLockoutPolicy) AppendEvents(events ...*es_models.Event) error {
for _, event := range events {
if err := p.AppendEvent(event); err != nil {
return err
}
}
return nil
}
func (p *PasswordLockoutPolicy) AppendEvent(event *es_models.Event) error {
p.ObjectRoot.AppendEvent(event)
switch event.Type {
case model.PasswordLockoutPolicyAdded, model.PasswordLockoutPolicyChanged:
if err := json.Unmarshal(event.Data, p); err != nil {
logging.Log("EVEN-idl93").WithError(err).Error("could not unmarshal event data")
return err
}
return nil
}
return nil
}

View File

@@ -0,0 +1,52 @@
package eventsourcing
import (
"context"
"github.com/caos/zitadel/internal/errors"
es_models "github.com/caos/zitadel/internal/eventstore/models"
"github.com/caos/zitadel/internal/policy/model"
)
func PasswordAgePolicyQuery(recourceOwner string, latestSequence uint64) *es_models.SearchQuery {
return es_models.NewSearchQuery().
AggregateTypeFilter(model.PasswordAgePolicyAggregate).
LatestSequenceFilter(latestSequence).
ResourceOwnerFilter(recourceOwner)
}
func PasswordAgePolicyAggregate(ctx context.Context, aggCreator *es_models.AggregateCreator, policy *PasswordAgePolicy) (*es_models.Aggregate, error) {
if policy == nil {
return nil, errors.ThrowPreconditionFailed(nil, "EVENT-1T05i", "existing policy should not be nil")
}
return aggCreator.NewAggregate(ctx, policy.AggregateID, model.PasswordAgePolicyAggregate, policyAgeVersion, policy.Sequence)
}
func PasswordAgePolicyCreateAggregate(aggCreator *es_models.AggregateCreator, policy *PasswordAgePolicy) func(ctx context.Context) (*es_models.Aggregate, error) {
return func(ctx context.Context) (*es_models.Aggregate, error) {
if policy == nil {
return nil, errors.ThrowPreconditionFailed(nil, "EVENT-kdie6", "policy should not be nil")
}
agg, err := PasswordAgePolicyAggregate(ctx, aggCreator, policy)
if err != nil {
return nil, err
}
return agg.AppendEvent(model.PasswordAgePolicyAdded, policy)
}
}
func PasswordAgePolicyUpdateAggregate(aggCreator *es_models.AggregateCreator, existing *PasswordAgePolicy, new *PasswordAgePolicy) func(ctx context.Context) (*es_models.Aggregate, error) {
return func(ctx context.Context) (*es_models.Aggregate, error) {
if new == nil {
return nil, errors.ThrowPreconditionFailed(nil, "EVENT-dhr74", "new policy should not be nil")
}
agg, err := PasswordAgePolicyAggregate(ctx, aggCreator, existing)
if err != nil {
return nil, err
}
changes := existing.AgeChanges(new)
return agg.AppendEvent(model.PasswordAgePolicyChanged, changes)
}
}

View File

@@ -0,0 +1,268 @@
package eventsourcing
import (
"context"
"testing"
"github.com/caos/zitadel/internal/api/auth"
caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore/models"
policy_model "github.com/caos/zitadel/internal/policy/model"
)
func TestGetPasswordAgePolicyQuery(t *testing.T) {
type args struct {
recourceOwner string
sequence uint64
}
type res struct {
filterLen int
wantErr bool
errFunc func(err error) bool
}
tests := []struct {
name string
args args
res res
}{
{
name: "Get password age policy query ok",
args: args{
recourceOwner: "org",
sequence: 14,
},
res: res{
filterLen: 3,
},
},
{
name: "Get password age policy query, no org",
args: args{
sequence: 1,
},
res: res{
filterLen: 3,
wantErr: true,
errFunc: caos_errs.IsPreconditionFailed,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
query := PasswordAgePolicyQuery(tt.args.recourceOwner, tt.args.sequence)
if !tt.res.wantErr && query == nil {
t.Errorf("query should not be nil")
}
if !tt.res.wantErr && len(query.Filters) != tt.res.filterLen {
t.Errorf("got wrong filter len: expected: %v, actual: %v ", tt.res.filterLen, len(query.Filters))
}
})
}
}
func TestPasswordAgePolicyAggregate(t *testing.T) {
type args struct {
ctx context.Context
aggCreator *models.AggregateCreator
policy *PasswordAgePolicy
}
type res struct {
eventLen int
aggType models.AggregateType
wantErr bool
errFunc func(err error) bool
}
tests := []struct {
name string
args args
res res
}{
{
name: "create aggregate",
args: args{
ctx: auth.NewMockContext("orgID", "userID"),
aggCreator: models.NewAggregateCreator("Test"),
policy: &PasswordAgePolicy{ObjectRoot: models.ObjectRoot{AggregateID: "AggregateID", Sequence: 1}, Description: "Test"},
},
res: res{
eventLen: 0,
aggType: policy_model.PasswordAgePolicyAggregate,
},
},
{
name: "policy nil",
args: args{
ctx: auth.NewMockContext("orgID", "userID"),
aggCreator: models.NewAggregateCreator("Test"),
},
res: res{
eventLen: 0,
aggType: policy_model.PasswordAgePolicyAggregate,
wantErr: true,
errFunc: caos_errs.IsPreconditionFailed,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
agg, err := PasswordAgePolicyAggregate(tt.args.ctx, tt.args.aggCreator, tt.args.policy)
if !tt.res.wantErr && agg == nil {
t.Errorf("agg should not be nil")
}
if !tt.res.wantErr && len(agg.Events) != tt.res.eventLen {
t.Errorf("got wrong event len: expected: %v, actual: %v ", tt.res.eventLen, len(agg.Events))
}
if tt.res.wantErr && !tt.res.errFunc(err) {
t.Errorf("got wrong err: %v ", err)
}
})
}
}
func TestPasswordAgePolicyCreateAggregate(t *testing.T) {
type args struct {
ctx context.Context
new *PasswordAgePolicy
aggCreator *models.AggregateCreator
}
type res struct {
eventLen int
eventType models.EventType
wantErr bool
errFunc func(err error) bool
}
tests := []struct {
name string
args args
res res
}{
{
name: "policy update aggregate ok",
args: args{
ctx: auth.NewMockContext("orgID", "userID"),
new: &PasswordAgePolicy{ObjectRoot: models.ObjectRoot{AggregateID: "AggregateID"}, Description: "PolicyName", State: int32(policy_model.POLICYSTATE_ACTIVE)},
aggCreator: models.NewAggregateCreator("Test"),
},
res: res{
eventLen: 1,
eventType: policy_model.PasswordAgePolicyAdded,
},
},
{
name: "new policy nil",
args: args{
ctx: auth.NewMockContext("orgID", "userID"),
new: nil,
aggCreator: models.NewAggregateCreator("Test"),
},
res: res{
eventLen: 1,
eventType: policy_model.PasswordAgePolicyAdded,
wantErr: true,
errFunc: caos_errs.IsPreconditionFailed,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
agg, err := PasswordAgePolicyCreateAggregate(tt.args.aggCreator, tt.args.new)(tt.args.ctx)
if !tt.res.wantErr && len(agg.Events) != tt.res.eventLen {
t.Errorf("got wrong event len: expected: %v, actual: %v ", tt.res.eventLen, len(agg.Events))
}
if !tt.res.wantErr && agg.Events[0].Type != tt.res.eventType {
t.Errorf("got wrong event type: expected: %v, actual: %v ", tt.res.eventType, agg.Events[0].Type.String())
}
if !tt.res.wantErr && agg.Events[0].Data == nil {
t.Errorf("should have data in event")
}
if tt.res.wantErr && !tt.res.errFunc(err) {
t.Errorf("got wrong err: %v ", err)
}
})
}
}
func TestPasswordAgePolicyUpdateAggregate(t *testing.T) {
type args struct {
ctx context.Context
existing *PasswordAgePolicy
new *PasswordAgePolicy
aggCreator *models.AggregateCreator
}
type res struct {
eventLen int
eventType models.EventType
wantErr bool
errFunc func(err error) bool
}
tests := []struct {
name string
args args
res res
}{
{
name: "policy update aggregate ok",
args: args{
ctx: auth.NewMockContext("orgID", "userID"),
existing: &PasswordAgePolicy{ObjectRoot: models.ObjectRoot{AggregateID: "AggregateID"}, Description: "PolicyName", State: int32(policy_model.POLICYSTATE_ACTIVE)},
new: &PasswordAgePolicy{ObjectRoot: models.ObjectRoot{AggregateID: "AggregateID"}, Description: "PolicyName_Changed", State: int32(policy_model.POLICYSTATE_ACTIVE)},
aggCreator: models.NewAggregateCreator("Test"),
},
res: res{
eventLen: 1,
eventType: policy_model.PasswordAgePolicyChanged,
},
},
{
name: "existing policy nil",
args: args{
ctx: auth.NewMockContext("orgID", "userID"),
existing: nil,
aggCreator: models.NewAggregateCreator("Test"),
},
res: res{
eventLen: 1,
eventType: policy_model.PasswordAgePolicyChanged,
wantErr: true,
errFunc: caos_errs.IsPreconditionFailed,
},
},
{
name: "new policy nil",
args: args{
ctx: auth.NewMockContext("orgID", "userID"),
existing: &PasswordAgePolicy{ObjectRoot: models.ObjectRoot{AggregateID: "AggregateID"}, Description: "ProjectName", State: int32(policy_model.POLICYSTATE_ACTIVE)},
new: nil,
aggCreator: models.NewAggregateCreator("Test"),
},
res: res{
eventLen: 1,
eventType: policy_model.PasswordAgePolicyChanged,
wantErr: true,
errFunc: caos_errs.IsPreconditionFailed,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
agg, err := PasswordAgePolicyUpdateAggregate(tt.args.aggCreator, tt.args.existing, tt.args.new)(tt.args.ctx)
if !tt.res.wantErr && len(agg.Events) != tt.res.eventLen {
t.Errorf("got wrong event len: expected: %v, actual: %v ", tt.res.eventLen, len(agg.Events))
}
if !tt.res.wantErr && agg.Events[0].Type != tt.res.eventType {
t.Errorf("got wrong event type: expected: %v, actual: %v ", tt.res.eventType, agg.Events[0].Type.String())
}
if !tt.res.wantErr && agg.Events[0].Data == nil {
t.Errorf("should have data in event")
}
if tt.res.wantErr && !tt.res.errFunc(err) {
t.Errorf("got wrong err: %v ", err)
}
})
}
}

View File

@@ -0,0 +1,52 @@
package eventsourcing
import (
"context"
"github.com/caos/zitadel/internal/errors"
es_models "github.com/caos/zitadel/internal/eventstore/models"
"github.com/caos/zitadel/internal/policy/model"
)
func PasswordComplexityPolicyQuery(recourceOwner string, latestSequence uint64) *es_models.SearchQuery {
return es_models.NewSearchQuery().
AggregateTypeFilter(model.PasswordComplexityPolicyAggregate).
LatestSequenceFilter(latestSequence).
ResourceOwnerFilter(recourceOwner)
}
func PasswordComplexityPolicyAggregate(ctx context.Context, aggCreator *es_models.AggregateCreator, policy *PasswordComplexityPolicy) (*es_models.Aggregate, error) {
if policy == nil {
return nil, errors.ThrowPreconditionFailed(nil, "EVENT-fRVr9", "existing policy should not be nil")
}
return aggCreator.NewAggregate(ctx, policy.AggregateID, model.PasswordComplexityPolicyAggregate, policyComplexityVersion, policy.Sequence)
}
func PasswordComplexityPolicyCreateAggregate(aggCreator *es_models.AggregateCreator, policy *PasswordComplexityPolicy) func(ctx context.Context) (*es_models.Aggregate, error) {
return func(ctx context.Context) (*es_models.Aggregate, error) {
if policy == nil {
return nil, errors.ThrowPreconditionFailed(nil, "EVENT-kdie6", "policy should not be nil")
}
agg, err := PasswordComplexityPolicyAggregate(ctx, aggCreator, policy)
if err != nil {
return nil, err
}
return agg.AppendEvent(model.PasswordComplexityPolicyAdded, policy)
}
}
func PasswordComplexityPolicyUpdateAggregate(aggCreator *es_models.AggregateCreator, existing *PasswordComplexityPolicy, new *PasswordComplexityPolicy) func(ctx context.Context) (*es_models.Aggregate, error) {
return func(ctx context.Context) (*es_models.Aggregate, error) {
if new == nil {
return nil, errors.ThrowPreconditionFailed(nil, "EVENT-dhr74", "new policy should not be nil")
}
agg, err := PasswordComplexityPolicyAggregate(ctx, aggCreator, existing)
if err != nil {
return nil, err
}
changes := existing.ComplexityChanges(new)
return agg.AppendEvent(model.PasswordComplexityPolicyChanged, changes)
}
}

View File

@@ -0,0 +1,268 @@
package eventsourcing
import (
"context"
"testing"
"github.com/caos/zitadel/internal/api/auth"
caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore/models"
policy_model "github.com/caos/zitadel/internal/policy/model"
)
func TestPasswordComplexityPolicyQuery(t *testing.T) {
type args struct {
recourceOwner string
sequence uint64
}
type res struct {
filterLen int
wantErr bool
errFunc func(err error) bool
}
tests := []struct {
name string
args args
res res
}{
{
name: "Get password complexity policy query ok",
args: args{
recourceOwner: "org",
sequence: 0,
},
res: res{
filterLen: 3,
},
},
{
name: "Get password complexity policy query, no org",
args: args{
sequence: 1,
},
res: res{
filterLen: 3,
wantErr: true,
errFunc: caos_errs.IsPreconditionFailed,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
query := PasswordComplexityPolicyQuery(tt.args.recourceOwner, tt.args.sequence)
if !tt.res.wantErr && query == nil {
t.Errorf("query should not be nil")
}
if !tt.res.wantErr && len(query.Filters) != tt.res.filterLen {
t.Errorf("got wrong filter len: expected: %v, actual: %v ", tt.res.filterLen, len(query.Filters))
}
})
}
}
func TestPasswordComplexityPolicyAggregate(t *testing.T) {
type args struct {
ctx context.Context
aggCreator *models.AggregateCreator
policy *PasswordComplexityPolicy
}
type res struct {
eventLen int
aggType models.AggregateType
wantErr bool
errFunc func(err error) bool
}
tests := []struct {
name string
args args
res res
}{
{
name: "create aggregate",
args: args{
ctx: auth.NewMockContext("orgID", "userID"),
aggCreator: models.NewAggregateCreator("Test"),
policy: &PasswordComplexityPolicy{ObjectRoot: models.ObjectRoot{AggregateID: "AggregateID", Sequence: 1}, Description: "Test"},
},
res: res{
eventLen: 0,
aggType: policy_model.PasswordComplexityPolicyAggregate,
},
},
{
name: "policy nil",
args: args{
ctx: auth.NewMockContext("orgID", "userID"),
aggCreator: models.NewAggregateCreator("Test"),
},
res: res{
eventLen: 0,
aggType: policy_model.PasswordComplexityPolicyAggregate,
wantErr: true,
errFunc: caos_errs.IsPreconditionFailed,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
agg, err := PasswordComplexityPolicyAggregate(tt.args.ctx, tt.args.aggCreator, tt.args.policy)
if !tt.res.wantErr && agg == nil {
t.Errorf("agg should not be nil")
}
if !tt.res.wantErr && len(agg.Events) != tt.res.eventLen {
t.Errorf("got wrong event len: expected: %v, actual: %v ", tt.res.eventLen, len(agg.Events))
}
if tt.res.wantErr && !tt.res.errFunc(err) {
t.Errorf("got wrong err: %v ", err)
}
})
}
}
func TestPasswordComplexityPolicyCreateAggregate(t *testing.T) {
type args struct {
ctx context.Context
new *PasswordComplexityPolicy
aggCreator *models.AggregateCreator
}
type res struct {
eventLen int
eventType models.EventType
wantErr bool
errFunc func(err error) bool
}
tests := []struct {
name string
args args
res res
}{
{
name: "policy update aggregate ok",
args: args{
ctx: auth.NewMockContext("orgID", "userID"),
new: &PasswordComplexityPolicy{ObjectRoot: models.ObjectRoot{AggregateID: "AggregateID"}, Description: "PolicyName", State: int32(policy_model.POLICYSTATE_ACTIVE)},
aggCreator: models.NewAggregateCreator("Test"),
},
res: res{
eventLen: 1,
eventType: policy_model.PasswordComplexityPolicyAdded,
},
},
{
name: "new policy nil",
args: args{
ctx: auth.NewMockContext("orgID", "userID"),
new: nil,
aggCreator: models.NewAggregateCreator("Test"),
},
res: res{
eventLen: 1,
eventType: policy_model.PasswordComplexityPolicyAdded,
wantErr: true,
errFunc: caos_errs.IsPreconditionFailed,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
agg, err := PasswordComplexityPolicyCreateAggregate(tt.args.aggCreator, tt.args.new)(tt.args.ctx)
if !tt.res.wantErr && len(agg.Events) != tt.res.eventLen {
t.Errorf("got wrong event len: expected: %v, actual: %v ", tt.res.eventLen, len(agg.Events))
}
if !tt.res.wantErr && agg.Events[0].Type != tt.res.eventType {
t.Errorf("got wrong event type: expected: %v, actual: %v ", tt.res.eventType, agg.Events[0].Type.String())
}
if !tt.res.wantErr && agg.Events[0].Data == nil {
t.Errorf("should have data in event")
}
if tt.res.wantErr && !tt.res.errFunc(err) {
t.Errorf("got wrong err: %v ", err)
}
})
}
}
func TestPasswordComplexityPolicyUpdateAggregate(t *testing.T) {
type args struct {
ctx context.Context
existing *PasswordComplexityPolicy
new *PasswordComplexityPolicy
aggCreator *models.AggregateCreator
}
type res struct {
eventLen int
eventType models.EventType
wantErr bool
errFunc func(err error) bool
}
tests := []struct {
name string
args args
res res
}{
{
name: "policy update aggregate ok",
args: args{
ctx: auth.NewMockContext("orgID", "userID"),
existing: &PasswordComplexityPolicy{ObjectRoot: models.ObjectRoot{AggregateID: "AggregateID"}, Description: "PolicyName", State: int32(policy_model.POLICYSTATE_ACTIVE)},
new: &PasswordComplexityPolicy{ObjectRoot: models.ObjectRoot{AggregateID: "AggregateID"}, Description: "PolicyName_Changed", State: int32(policy_model.POLICYSTATE_ACTIVE)},
aggCreator: models.NewAggregateCreator("Test"),
},
res: res{
eventLen: 1,
eventType: policy_model.PasswordComplexityPolicyChanged,
},
},
{
name: "existing policy nil",
args: args{
ctx: auth.NewMockContext("orgID", "userID"),
existing: nil,
aggCreator: models.NewAggregateCreator("Test"),
},
res: res{
eventLen: 1,
eventType: policy_model.PasswordComplexityPolicyChanged,
wantErr: true,
errFunc: caos_errs.IsPreconditionFailed,
},
},
{
name: "new policy nil",
args: args{
ctx: auth.NewMockContext("orgID", "userID"),
existing: &PasswordComplexityPolicy{ObjectRoot: models.ObjectRoot{AggregateID: "AggregateID"}, Description: "ProjectName", State: int32(policy_model.POLICYSTATE_ACTIVE)},
new: nil,
aggCreator: models.NewAggregateCreator("Test"),
},
res: res{
eventLen: 1,
eventType: policy_model.PasswordComplexityPolicyChanged,
wantErr: true,
errFunc: caos_errs.IsPreconditionFailed,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
agg, err := PasswordComplexityPolicyUpdateAggregate(tt.args.aggCreator, tt.args.existing, tt.args.new)(tt.args.ctx)
if !tt.res.wantErr && len(agg.Events) != tt.res.eventLen {
t.Errorf("got wrong event len: expected: %v, actual: %v ", tt.res.eventLen, len(agg.Events))
}
if !tt.res.wantErr && agg.Events[0].Type != tt.res.eventType {
t.Errorf("got wrong event type: expected: %v, actual: %v ", tt.res.eventType, agg.Events[0].Type.String())
}
if !tt.res.wantErr && agg.Events[0].Data == nil {
t.Errorf("should have data in event")
}
if tt.res.wantErr && !tt.res.errFunc(err) {
t.Errorf("got wrong err: %v ", err)
}
})
}
}

View File

@@ -0,0 +1,53 @@
package eventsourcing
import (
"context"
"github.com/caos/zitadel/internal/errors"
es_models "github.com/caos/zitadel/internal/eventstore/models"
"github.com/caos/zitadel/internal/policy/model"
)
func PasswordLockoutPolicyQuery(recourceOwner string, latestSequence uint64) *es_models.SearchQuery {
return es_models.NewSearchQuery().
AggregateTypeFilter(model.PasswordLockoutPolicyAggregate).
LatestSequenceFilter(latestSequence).
ResourceOwnerFilter(recourceOwner)
}
func PasswordLockoutPolicyAggregate(ctx context.Context, aggCreator *es_models.AggregateCreator, policy *PasswordLockoutPolicy) (*es_models.Aggregate, error) {
if policy == nil {
return nil, errors.ThrowPreconditionFailed(nil, "EVENT-aTRlj", "existing policy should not be nil")
}
return aggCreator.NewAggregate(ctx, policy.AggregateID, model.PasswordLockoutPolicyAggregate, policyLockoutVersion, policy.Sequence)
}
func PasswordLockoutPolicyCreateAggregate(aggCreator *es_models.AggregateCreator, policy *PasswordLockoutPolicy) func(ctx context.Context) (*es_models.Aggregate, error) {
return func(ctx context.Context) (*es_models.Aggregate, error) {
if policy == nil {
return nil, errors.ThrowPreconditionFailed(nil, "EVENT-kdie6", "policy should not be nil")
}
agg, err := PasswordLockoutPolicyAggregate(ctx, aggCreator, policy)
if err != nil {
return nil, err
}
return agg.AppendEvent(model.PasswordLockoutPolicyAdded, policy)
}
}
func PasswordLockoutPolicyUpdateAggregate(aggCreator *es_models.AggregateCreator, existing *PasswordLockoutPolicy, new *PasswordLockoutPolicy) func(ctx context.Context) (*es_models.Aggregate, error) {
return func(ctx context.Context) (*es_models.Aggregate, error) {
if new == nil {
return nil, errors.ThrowPreconditionFailed(nil, "EVENT-dhr74", "new policy should not be nil")
}
agg, err := PasswordLockoutPolicyAggregate(ctx, aggCreator, existing)
if err != nil {
return nil, err
}
changes := existing.LockoutChanges(new)
return agg.AppendEvent(model.PasswordLockoutPolicyChanged, changes)
}
}

View File

@@ -0,0 +1,268 @@
package eventsourcing
import (
"context"
"testing"
"github.com/caos/zitadel/internal/api/auth"
caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore/models"
policy_model "github.com/caos/zitadel/internal/policy/model"
)
func TestGetPasswordLockoutPolicyQuery(t *testing.T) {
type args struct {
recourceOwner string
sequence uint64
}
type res struct {
filterLen int
wantErr bool
errFunc func(err error) bool
}
tests := []struct {
name string
args args
res res
}{
{
name: "Get password lockout policy query ok",
args: args{
recourceOwner: "org",
sequence: 14,
},
res: res{
filterLen: 3,
},
},
{
name: "Get password lockout policy query, no org",
args: args{
sequence: 1,
},
res: res{
filterLen: 3,
wantErr: true,
errFunc: caos_errs.IsPreconditionFailed,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
query := PasswordLockoutPolicyQuery(tt.args.recourceOwner, tt.args.sequence)
if !tt.res.wantErr && query == nil {
t.Errorf("query should not be nil")
}
if !tt.res.wantErr && len(query.Filters) != tt.res.filterLen {
t.Errorf("got wrong filter len: expected: %v, actual: %v ", tt.res.filterLen, len(query.Filters))
}
})
}
}
func TestPasswordLockoutPolicyAggregate(t *testing.T) {
type args struct {
ctx context.Context
aggCreator *models.AggregateCreator
policy *PasswordLockoutPolicy
}
type res struct {
eventLen int
aggType models.AggregateType
wantErr bool
errFunc func(err error) bool
}
tests := []struct {
name string
args args
res res
}{
{
name: "create aggregate",
args: args{
ctx: auth.NewMockContext("orgID", "userID"),
aggCreator: models.NewAggregateCreator("Test"),
policy: &PasswordLockoutPolicy{ObjectRoot: models.ObjectRoot{AggregateID: "AggregateID", Sequence: 1}, Description: "Test"},
},
res: res{
eventLen: 0,
aggType: policy_model.PasswordLockoutPolicyAggregate,
},
},
{
name: "policy nil",
args: args{
ctx: auth.NewMockContext("orgID", "userID"),
aggCreator: models.NewAggregateCreator("Test"),
},
res: res{
eventLen: 0,
aggType: policy_model.PasswordLockoutPolicyAggregate,
wantErr: true,
errFunc: caos_errs.IsPreconditionFailed,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
agg, err := PasswordLockoutPolicyAggregate(tt.args.ctx, tt.args.aggCreator, tt.args.policy)
if !tt.res.wantErr && agg == nil {
t.Errorf("agg should not be nil")
}
if !tt.res.wantErr && len(agg.Events) != tt.res.eventLen {
t.Errorf("got wrong event len: expected: %v, actual: %v ", tt.res.eventLen, len(agg.Events))
}
if tt.res.wantErr && !tt.res.errFunc(err) {
t.Errorf("got wrong err: %v ", err)
}
})
}
}
func TestPasswordLockoutPolicyCreateAggregate(t *testing.T) {
type args struct {
ctx context.Context
new *PasswordLockoutPolicy
aggCreator *models.AggregateCreator
}
type res struct {
eventLen int
eventType models.EventType
wantErr bool
errFunc func(err error) bool
}
tests := []struct {
name string
args args
res res
}{
{
name: "policy update aggregate ok",
args: args{
ctx: auth.NewMockContext("orgID", "userID"),
new: &PasswordLockoutPolicy{ObjectRoot: models.ObjectRoot{AggregateID: "AggregateID"}, Description: "PolicyName", State: int32(policy_model.POLICYSTATE_ACTIVE)},
aggCreator: models.NewAggregateCreator("Test"),
},
res: res{
eventLen: 1,
eventType: policy_model.PasswordLockoutPolicyAdded,
},
},
{
name: "new policy nil",
args: args{
ctx: auth.NewMockContext("orgID", "userID"),
new: nil,
aggCreator: models.NewAggregateCreator("Test"),
},
res: res{
eventLen: 1,
eventType: policy_model.PasswordLockoutPolicyAdded,
wantErr: true,
errFunc: caos_errs.IsPreconditionFailed,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
agg, err := PasswordLockoutPolicyCreateAggregate(tt.args.aggCreator, tt.args.new)(tt.args.ctx)
if !tt.res.wantErr && len(agg.Events) != tt.res.eventLen {
t.Errorf("got wrong event len: expected: %v, actual: %v ", tt.res.eventLen, len(agg.Events))
}
if !tt.res.wantErr && agg.Events[0].Type != tt.res.eventType {
t.Errorf("got wrong event type: expected: %v, actual: %v ", tt.res.eventType, agg.Events[0].Type.String())
}
if !tt.res.wantErr && agg.Events[0].Data == nil {
t.Errorf("should have data in event")
}
if tt.res.wantErr && !tt.res.errFunc(err) {
t.Errorf("got wrong err: %v ", err)
}
})
}
}
func TestPasswordLockoutPolicyUpdateAggregate(t *testing.T) {
type args struct {
ctx context.Context
existing *PasswordLockoutPolicy
new *PasswordLockoutPolicy
aggCreator *models.AggregateCreator
}
type res struct {
eventLen int
eventType models.EventType
wantErr bool
errFunc func(err error) bool
}
tests := []struct {
name string
args args
res res
}{
{
name: "policy update aggregate ok",
args: args{
ctx: auth.NewMockContext("orgID", "userID"),
existing: &PasswordLockoutPolicy{ObjectRoot: models.ObjectRoot{AggregateID: "AggregateID"}, Description: "PolicyName", State: int32(policy_model.POLICYSTATE_ACTIVE)},
new: &PasswordLockoutPolicy{ObjectRoot: models.ObjectRoot{AggregateID: "AggregateID"}, Description: "PolicyName_Changed", State: int32(policy_model.POLICYSTATE_ACTIVE)},
aggCreator: models.NewAggregateCreator("Test"),
},
res: res{
eventLen: 1,
eventType: policy_model.PasswordLockoutPolicyChanged,
},
},
{
name: "existing policy nil",
args: args{
ctx: auth.NewMockContext("orgID", "userID"),
existing: nil,
aggCreator: models.NewAggregateCreator("Test"),
},
res: res{
eventLen: 1,
eventType: policy_model.PasswordLockoutPolicyChanged,
wantErr: true,
errFunc: caos_errs.IsPreconditionFailed,
},
},
{
name: "new policy nil",
args: args{
ctx: auth.NewMockContext("orgID", "userID"),
existing: &PasswordLockoutPolicy{ObjectRoot: models.ObjectRoot{AggregateID: "AggregateID"}, Description: "ProjectName", State: int32(policy_model.POLICYSTATE_ACTIVE)},
new: nil,
aggCreator: models.NewAggregateCreator("Test"),
},
res: res{
eventLen: 1,
eventType: policy_model.PasswordLockoutPolicyChanged,
wantErr: true,
errFunc: caos_errs.IsPreconditionFailed,
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
agg, err := PasswordLockoutPolicyUpdateAggregate(tt.args.aggCreator, tt.args.existing, tt.args.new)(tt.args.ctx)
if !tt.res.wantErr && len(agg.Events) != tt.res.eventLen {
t.Errorf("got wrong event len: expected: %v, actual: %v ", tt.res.eventLen, len(agg.Events))
}
if !tt.res.wantErr && agg.Events[0].Type != tt.res.eventType {
t.Errorf("got wrong event type: expected: %v, actual: %v ", tt.res.eventType, agg.Events[0].Type.String())
}
if !tt.res.wantErr && agg.Events[0].Data == nil {
t.Errorf("should have data in event")
}
if tt.res.wantErr && !tt.res.errFunc(err) {
t.Errorf("got wrong err: %v ", err)
}
})
}
}

View File

@@ -83,7 +83,7 @@ func templatesAuth_method_mappingGoTmpl() (*asset, error) {
return nil, err
}
info := bindataFileInfo{name: "templates/auth_method_mapping.go.tmpl", size: 1013, mode: os.FileMode(420), modTime: time.Unix(1586159062, 0)}
info := bindataFileInfo{name: "templates/auth_method_mapping.go.tmpl", size: 1013, mode: os.FileMode(420), modTime: time.Unix(1586333177, 0)}
a := &asset{bytes: bytes, info: info}
return a, nil
}