mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-12 03:37:34 +00:00
feat: Lockout policy (#2121)
* feat: lock users if lockout policy is set * feat: setup * feat: lock user on password failes * feat: render error * feat: lock user on command side * feat: auth_req tests * feat: lockout policy docs * feat: remove show lockout failures from proto * fix: console lockout * feat: tests * fix: tests * unlock function * add unlock button * fix migration version * lockout policy * lint * Update internal/auth/repository/eventsourcing/eventstore/auth_request.go Co-authored-by: Silvan <silvan.reusser@gmail.com> * fix: err message * Update internal/command/setup_step4.go Co-authored-by: Silvan <silvan.reusser@gmail.com> Co-authored-by: Max Peintner <max@caos.ch> Co-authored-by: Livio Amstutz <livio.a@gmail.com> Co-authored-by: Silvan <silvan.reusser@gmail.com>
This commit is contained in:
@@ -112,10 +112,10 @@ func writeModelToPasswordComplexityPolicy(wm *PasswordComplexityPolicyWriteModel
|
||||
}
|
||||
}
|
||||
|
||||
func writeModelToPasswordLockoutPolicy(wm *PasswordLockoutPolicyWriteModel) *domain.PasswordLockoutPolicy {
|
||||
return &domain.PasswordLockoutPolicy{
|
||||
func writeModelToLockoutPolicy(wm *LockoutPolicyWriteModel) *domain.LockoutPolicy {
|
||||
return &domain.LockoutPolicy{
|
||||
ObjectRoot: writeModelToObjectRoot(wm.WriteModel),
|
||||
MaxAttempts: wm.MaxAttempts,
|
||||
MaxPasswordAttempts: wm.MaxPasswordAttempts,
|
||||
ShowLockOutFailures: wm.ShowLockOutFailures,
|
||||
}
|
||||
}
|
||||
|
@@ -9,10 +9,10 @@ import (
|
||||
"github.com/caos/zitadel/internal/telemetry/tracing"
|
||||
)
|
||||
|
||||
func (c *Commands) AddDefaultPasswordLockoutPolicy(ctx context.Context, policy *domain.PasswordLockoutPolicy) (*domain.PasswordLockoutPolicy, error) {
|
||||
addedPolicy := NewIAMPasswordLockoutPolicyWriteModel()
|
||||
func (c *Commands) AddDefaultLockoutPolicy(ctx context.Context, policy *domain.LockoutPolicy) (*domain.LockoutPolicy, error) {
|
||||
addedPolicy := NewIAMLockoutPolicyWriteModel()
|
||||
iamAgg := IAMAggregateFromWriteModel(&addedPolicy.WriteModel)
|
||||
event, err := c.addDefaultPasswordLockoutPolicy(ctx, iamAgg, addedPolicy, policy)
|
||||
event, err := c.addDefaultLockoutPolicy(ctx, iamAgg, addedPolicy, policy)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -25,34 +25,34 @@ func (c *Commands) AddDefaultPasswordLockoutPolicy(ctx context.Context, policy *
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return writeModelToPasswordLockoutPolicy(&addedPolicy.PasswordLockoutPolicyWriteModel), nil
|
||||
return writeModelToLockoutPolicy(&addedPolicy.LockoutPolicyWriteModel), nil
|
||||
}
|
||||
|
||||
func (c *Commands) addDefaultPasswordLockoutPolicy(ctx context.Context, iamAgg *eventstore.Aggregate, addedPolicy *IAMPasswordLockoutPolicyWriteModel, policy *domain.PasswordLockoutPolicy) (eventstore.EventPusher, error) {
|
||||
func (c *Commands) addDefaultLockoutPolicy(ctx context.Context, iamAgg *eventstore.Aggregate, addedPolicy *IAMLockoutPolicyWriteModel, policy *domain.LockoutPolicy) (eventstore.EventPusher, error) {
|
||||
err := c.eventstore.FilterToQueryReducer(ctx, addedPolicy)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if addedPolicy.State == domain.PolicyStateActive {
|
||||
return nil, caos_errs.ThrowAlreadyExists(nil, "IAM-0olDf", "Errors.IAM.PasswordLockoutPolicy.AlreadyExists")
|
||||
return nil, caos_errs.ThrowAlreadyExists(nil, "IAM-0olDf", "Errors.IAM.LockoutPolicy.AlreadyExists")
|
||||
}
|
||||
|
||||
return iam_repo.NewPasswordLockoutPolicyAddedEvent(ctx, iamAgg, policy.MaxAttempts, policy.ShowLockOutFailures), nil
|
||||
return iam_repo.NewLockoutPolicyAddedEvent(ctx, iamAgg, policy.MaxPasswordAttempts, policy.ShowLockOutFailures), nil
|
||||
}
|
||||
|
||||
func (c *Commands) ChangeDefaultPasswordLockoutPolicy(ctx context.Context, policy *domain.PasswordLockoutPolicy) (*domain.PasswordLockoutPolicy, error) {
|
||||
existingPolicy, err := c.defaultPasswordLockoutPolicyWriteModelByID(ctx)
|
||||
func (c *Commands) ChangeDefaultLockoutPolicy(ctx context.Context, policy *domain.LockoutPolicy) (*domain.LockoutPolicy, error) {
|
||||
existingPolicy, err := c.defaultLockoutPolicyWriteModelByID(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
|
||||
return nil, caos_errs.ThrowNotFound(nil, "IAM-0oPew", "Errors.IAM.PasswordLockoutPolicy.NotFound")
|
||||
return nil, caos_errs.ThrowNotFound(nil, "IAM-0oPew", "Errors.IAM.LockoutPolicy.NotFound")
|
||||
}
|
||||
|
||||
iamAgg := IAMAggregateFromWriteModel(&existingPolicy.PasswordLockoutPolicyWriteModel.WriteModel)
|
||||
changedEvent, hasChanged := existingPolicy.NewChangedEvent(ctx, iamAgg, policy.MaxAttempts, policy.ShowLockOutFailures)
|
||||
iamAgg := IAMAggregateFromWriteModel(&existingPolicy.LockoutPolicyWriteModel.WriteModel)
|
||||
changedEvent, hasChanged := existingPolicy.NewChangedEvent(ctx, iamAgg, policy.MaxPasswordAttempts, policy.ShowLockOutFailures)
|
||||
if !hasChanged {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "IAM-4M9vs", "Errors.IAM.PasswordLockoutPolicy.NotChanged")
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "IAM-4M9vs", "Errors.IAM.LockoutPolicy.NotChanged")
|
||||
}
|
||||
|
||||
pushedEvents, err := c.eventstore.PushEvents(ctx, changedEvent)
|
||||
@@ -63,14 +63,14 @@ func (c *Commands) ChangeDefaultPasswordLockoutPolicy(ctx context.Context, polic
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return writeModelToPasswordLockoutPolicy(&existingPolicy.PasswordLockoutPolicyWriteModel), nil
|
||||
return writeModelToLockoutPolicy(&existingPolicy.LockoutPolicyWriteModel), nil
|
||||
}
|
||||
|
||||
func (c *Commands) defaultPasswordLockoutPolicyWriteModelByID(ctx context.Context) (policy *IAMPasswordLockoutPolicyWriteModel, err error) {
|
||||
func (c *Commands) defaultLockoutPolicyWriteModelByID(ctx context.Context) (policy *IAMLockoutPolicyWriteModel, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
writeModel := NewIAMPasswordLockoutPolicyWriteModel()
|
||||
writeModel := NewIAMLockoutPolicyWriteModel()
|
||||
err = c.eventstore.FilterToQueryReducer(ctx, writeModel)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@@ -10,13 +10,13 @@ import (
|
||||
"github.com/caos/zitadel/internal/repository/policy"
|
||||
)
|
||||
|
||||
type IAMPasswordLockoutPolicyWriteModel struct {
|
||||
PasswordLockoutPolicyWriteModel
|
||||
type IAMLockoutPolicyWriteModel struct {
|
||||
LockoutPolicyWriteModel
|
||||
}
|
||||
|
||||
func NewIAMPasswordLockoutPolicyWriteModel() *IAMPasswordLockoutPolicyWriteModel {
|
||||
return &IAMPasswordLockoutPolicyWriteModel{
|
||||
PasswordLockoutPolicyWriteModel{
|
||||
func NewIAMLockoutPolicyWriteModel() *IAMLockoutPolicyWriteModel {
|
||||
return &IAMLockoutPolicyWriteModel{
|
||||
LockoutPolicyWriteModel{
|
||||
WriteModel: eventstore.WriteModel{
|
||||
AggregateID: domain.IAMID,
|
||||
ResourceOwner: domain.IAMID,
|
||||
@@ -25,40 +25,40 @@ func NewIAMPasswordLockoutPolicyWriteModel() *IAMPasswordLockoutPolicyWriteModel
|
||||
}
|
||||
}
|
||||
|
||||
func (wm *IAMPasswordLockoutPolicyWriteModel) AppendEvents(events ...eventstore.EventReader) {
|
||||
func (wm *IAMLockoutPolicyWriteModel) AppendEvents(events ...eventstore.EventReader) {
|
||||
for _, event := range events {
|
||||
switch e := event.(type) {
|
||||
case *iam.PasswordLockoutPolicyAddedEvent:
|
||||
wm.PasswordLockoutPolicyWriteModel.AppendEvents(&e.PasswordLockoutPolicyAddedEvent)
|
||||
case *iam.PasswordLockoutPolicyChangedEvent:
|
||||
wm.PasswordLockoutPolicyWriteModel.AppendEvents(&e.PasswordLockoutPolicyChangedEvent)
|
||||
case *iam.LockoutPolicyAddedEvent:
|
||||
wm.LockoutPolicyWriteModel.AppendEvents(&e.LockoutPolicyAddedEvent)
|
||||
case *iam.LockoutPolicyChangedEvent:
|
||||
wm.LockoutPolicyWriteModel.AppendEvents(&e.LockoutPolicyChangedEvent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (wm *IAMPasswordLockoutPolicyWriteModel) Reduce() error {
|
||||
return wm.PasswordLockoutPolicyWriteModel.Reduce()
|
||||
func (wm *IAMLockoutPolicyWriteModel) Reduce() error {
|
||||
return wm.LockoutPolicyWriteModel.Reduce()
|
||||
}
|
||||
|
||||
func (wm *IAMPasswordLockoutPolicyWriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||
func (wm *IAMLockoutPolicyWriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent).
|
||||
ResourceOwner(wm.ResourceOwner).
|
||||
AddQuery().
|
||||
AggregateTypes(iam.AggregateType).
|
||||
AggregateIDs(wm.PasswordLockoutPolicyWriteModel.AggregateID).
|
||||
AggregateIDs(wm.LockoutPolicyWriteModel.AggregateID).
|
||||
EventTypes(
|
||||
iam.PasswordLockoutPolicyAddedEventType,
|
||||
iam.PasswordLockoutPolicyChangedEventType).
|
||||
iam.LockoutPolicyAddedEventType,
|
||||
iam.LockoutPolicyChangedEventType).
|
||||
Builder()
|
||||
}
|
||||
|
||||
func (wm *IAMPasswordLockoutPolicyWriteModel) NewChangedEvent(
|
||||
func (wm *IAMLockoutPolicyWriteModel) NewChangedEvent(
|
||||
ctx context.Context,
|
||||
aggregate *eventstore.Aggregate,
|
||||
maxAttempts uint64,
|
||||
showLockoutFailure bool) (*iam.PasswordLockoutPolicyChangedEvent, bool) {
|
||||
changes := make([]policy.PasswordLockoutPolicyChanges, 0)
|
||||
if wm.MaxAttempts != maxAttempts {
|
||||
showLockoutFailure bool) (*iam.LockoutPolicyChangedEvent, bool) {
|
||||
changes := make([]policy.LockoutPolicyChanges, 0)
|
||||
if wm.MaxPasswordAttempts != maxAttempts {
|
||||
changes = append(changes, policy.ChangeMaxAttempts(maxAttempts))
|
||||
}
|
||||
if wm.ShowLockOutFailures != showLockoutFailure {
|
||||
@@ -67,7 +67,7 @@ func (wm *IAMPasswordLockoutPolicyWriteModel) NewChangedEvent(
|
||||
if len(changes) == 0 {
|
||||
return nil, false
|
||||
}
|
||||
changedEvent, err := iam.NewPasswordLockoutPolicyChangedEvent(ctx, aggregate, changes)
|
||||
changedEvent, err := iam.NewLockoutPolicyChangedEvent(ctx, aggregate, changes)
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
|
@@ -13,16 +13,16 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCommandSide_AddDefaultPasswordLockoutPolicy(t *testing.T) {
|
||||
func TestCommandSide_AddDefaultLockoutPolicy(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
policy *domain.PasswordLockoutPolicy
|
||||
policy *domain.LockoutPolicy
|
||||
}
|
||||
type res struct {
|
||||
want *domain.PasswordLockoutPolicy
|
||||
want *domain.LockoutPolicy
|
||||
err func(error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
@@ -32,13 +32,13 @@ func TestCommandSide_AddDefaultPasswordLockoutPolicy(t *testing.T) {
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "password lockout policy already existing, already exists error",
|
||||
name: "lockout policy already existing, already exists error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
iam.NewPasswordLockoutPolicyAddedEvent(context.Background(),
|
||||
iam.NewLockoutPolicyAddedEvent(context.Background(),
|
||||
&iam.NewAggregate().Aggregate,
|
||||
10,
|
||||
true,
|
||||
@@ -49,8 +49,8 @@ func TestCommandSide_AddDefaultPasswordLockoutPolicy(t *testing.T) {
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
policy: &domain.PasswordLockoutPolicy{
|
||||
MaxAttempts: 10,
|
||||
policy: &domain.LockoutPolicy{
|
||||
MaxPasswordAttempts: 10,
|
||||
ShowLockOutFailures: true,
|
||||
},
|
||||
},
|
||||
@@ -67,7 +67,7 @@ func TestCommandSide_AddDefaultPasswordLockoutPolicy(t *testing.T) {
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(
|
||||
iam.NewPasswordLockoutPolicyAddedEvent(context.Background(),
|
||||
iam.NewLockoutPolicyAddedEvent(context.Background(),
|
||||
&iam.NewAggregate().Aggregate,
|
||||
10,
|
||||
true,
|
||||
@@ -79,18 +79,18 @@ func TestCommandSide_AddDefaultPasswordLockoutPolicy(t *testing.T) {
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
policy: &domain.PasswordLockoutPolicy{
|
||||
MaxAttempts: 10,
|
||||
policy: &domain.LockoutPolicy{
|
||||
MaxPasswordAttempts: 10,
|
||||
ShowLockOutFailures: true,
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
want: &domain.PasswordLockoutPolicy{
|
||||
want: &domain.LockoutPolicy{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "IAM",
|
||||
ResourceOwner: "IAM",
|
||||
},
|
||||
MaxAttempts: 10,
|
||||
MaxPasswordAttempts: 10,
|
||||
ShowLockOutFailures: true,
|
||||
},
|
||||
},
|
||||
@@ -101,7 +101,7 @@ func TestCommandSide_AddDefaultPasswordLockoutPolicy(t *testing.T) {
|
||||
r := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
}
|
||||
got, err := r.AddDefaultPasswordLockoutPolicy(tt.args.ctx, tt.args.policy)
|
||||
got, err := r.AddDefaultLockoutPolicy(tt.args.ctx, tt.args.policy)
|
||||
if tt.res.err == nil {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
@@ -115,16 +115,16 @@ func TestCommandSide_AddDefaultPasswordLockoutPolicy(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommandSide_ChangeDefaultPasswordLockoutPolicy(t *testing.T) {
|
||||
func TestCommandSide_ChangeDefaultLockoutPolicy(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
policy *domain.PasswordLockoutPolicy
|
||||
policy *domain.LockoutPolicy
|
||||
}
|
||||
type res struct {
|
||||
want *domain.PasswordLockoutPolicy
|
||||
want *domain.LockoutPolicy
|
||||
err func(error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
@@ -134,7 +134,7 @@ func TestCommandSide_ChangeDefaultPasswordLockoutPolicy(t *testing.T) {
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "password lockout policy not existing, not found error",
|
||||
name: "lockout policy not existing, not found error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
@@ -143,8 +143,8 @@ func TestCommandSide_ChangeDefaultPasswordLockoutPolicy(t *testing.T) {
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
policy: &domain.PasswordLockoutPolicy{
|
||||
MaxAttempts: 10,
|
||||
policy: &domain.LockoutPolicy{
|
||||
MaxPasswordAttempts: 10,
|
||||
ShowLockOutFailures: true,
|
||||
},
|
||||
},
|
||||
@@ -159,7 +159,7 @@ func TestCommandSide_ChangeDefaultPasswordLockoutPolicy(t *testing.T) {
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
iam.NewPasswordLockoutPolicyAddedEvent(context.Background(),
|
||||
iam.NewLockoutPolicyAddedEvent(context.Background(),
|
||||
&iam.NewAggregate().Aggregate,
|
||||
10,
|
||||
true,
|
||||
@@ -170,8 +170,8 @@ func TestCommandSide_ChangeDefaultPasswordLockoutPolicy(t *testing.T) {
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
policy: &domain.PasswordLockoutPolicy{
|
||||
MaxAttempts: 10,
|
||||
policy: &domain.LockoutPolicy{
|
||||
MaxPasswordAttempts: 10,
|
||||
ShowLockOutFailures: true,
|
||||
},
|
||||
},
|
||||
@@ -186,7 +186,7 @@ func TestCommandSide_ChangeDefaultPasswordLockoutPolicy(t *testing.T) {
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
iam.NewPasswordLockoutPolicyAddedEvent(context.Background(),
|
||||
iam.NewLockoutPolicyAddedEvent(context.Background(),
|
||||
&iam.NewAggregate().Aggregate,
|
||||
10,
|
||||
true,
|
||||
@@ -196,7 +196,7 @@ func TestCommandSide_ChangeDefaultPasswordLockoutPolicy(t *testing.T) {
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(
|
||||
newDefaultPasswordLockoutPolicyChangedEvent(context.Background(), 20, false),
|
||||
newDefaultLockoutPolicyChangedEvent(context.Background(), 20, false),
|
||||
),
|
||||
},
|
||||
),
|
||||
@@ -204,18 +204,18 @@ func TestCommandSide_ChangeDefaultPasswordLockoutPolicy(t *testing.T) {
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
policy: &domain.PasswordLockoutPolicy{
|
||||
MaxAttempts: 20,
|
||||
policy: &domain.LockoutPolicy{
|
||||
MaxPasswordAttempts: 20,
|
||||
ShowLockOutFailures: false,
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
want: &domain.PasswordLockoutPolicy{
|
||||
want: &domain.LockoutPolicy{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "IAM",
|
||||
ResourceOwner: "IAM",
|
||||
},
|
||||
MaxAttempts: 20,
|
||||
MaxPasswordAttempts: 20,
|
||||
ShowLockOutFailures: false,
|
||||
},
|
||||
},
|
||||
@@ -226,7 +226,7 @@ func TestCommandSide_ChangeDefaultPasswordLockoutPolicy(t *testing.T) {
|
||||
r := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
}
|
||||
got, err := r.ChangeDefaultPasswordLockoutPolicy(tt.args.ctx, tt.args.policy)
|
||||
got, err := r.ChangeDefaultLockoutPolicy(tt.args.ctx, tt.args.policy)
|
||||
if tt.res.err == nil {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
@@ -240,10 +240,10 @@ func TestCommandSide_ChangeDefaultPasswordLockoutPolicy(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func newDefaultPasswordLockoutPolicyChangedEvent(ctx context.Context, maxAttempts uint64, showLockoutFailure bool) *iam.PasswordLockoutPolicyChangedEvent {
|
||||
event, _ := iam.NewPasswordLockoutPolicyChangedEvent(ctx,
|
||||
func newDefaultLockoutPolicyChangedEvent(ctx context.Context, maxAttempts uint64, showLockoutFailure bool) *iam.LockoutPolicyChangedEvent {
|
||||
event, _ := iam.NewLockoutPolicyChangedEvent(ctx,
|
||||
&iam.NewAggregate().Aggregate,
|
||||
[]policy.PasswordLockoutPolicyChanges{
|
||||
[]policy.LockoutPolicyChanges{
|
||||
policy.ChangeMaxAttempts(maxAttempts),
|
||||
policy.ChangeShowLockOutFailures(showLockoutFailure),
|
||||
},
|
||||
|
@@ -7,21 +7,21 @@ import (
|
||||
"github.com/caos/zitadel/internal/repository/org"
|
||||
)
|
||||
|
||||
func (c *Commands) AddPasswordLockoutPolicy(ctx context.Context, resourceOwner string, policy *domain.PasswordLockoutPolicy) (*domain.PasswordLockoutPolicy, error) {
|
||||
func (c *Commands) AddLockoutPolicy(ctx context.Context, resourceOwner string, policy *domain.LockoutPolicy) (*domain.LockoutPolicy, error) {
|
||||
if resourceOwner == "" {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-8fJif", "Errors.ResourceOwnerMissing")
|
||||
}
|
||||
addedPolicy := NewOrgPasswordLockoutPolicyWriteModel(resourceOwner)
|
||||
addedPolicy := NewOrgLockoutPolicyWriteModel(resourceOwner)
|
||||
err := c.eventstore.FilterToQueryReducer(ctx, addedPolicy)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if addedPolicy.State == domain.PolicyStateActive {
|
||||
return nil, caos_errs.ThrowAlreadyExists(nil, "ORG-0olDf", "Errors.ORG.PasswordLockoutPolicy.AlreadyExists")
|
||||
return nil, caos_errs.ThrowAlreadyExists(nil, "ORG-0olDf", "Errors.ORG.LockoutPolicy.AlreadyExists")
|
||||
}
|
||||
|
||||
orgAgg := OrgAggregateFromWriteModel(&addedPolicy.WriteModel)
|
||||
pushedEvents, err := c.eventstore.PushEvents(ctx, org.NewPasswordLockoutPolicyAddedEvent(ctx, orgAgg, policy.MaxAttempts, policy.ShowLockOutFailures))
|
||||
pushedEvents, err := c.eventstore.PushEvents(ctx, org.NewLockoutPolicyAddedEvent(ctx, orgAgg, policy.MaxPasswordAttempts, policy.ShowLockOutFailures))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -29,26 +29,26 @@ func (c *Commands) AddPasswordLockoutPolicy(ctx context.Context, resourceOwner s
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return writeModelToPasswordLockoutPolicy(&addedPolicy.PasswordLockoutPolicyWriteModel), nil
|
||||
return writeModelToLockoutPolicy(&addedPolicy.LockoutPolicyWriteModel), nil
|
||||
}
|
||||
|
||||
func (c *Commands) ChangePasswordLockoutPolicy(ctx context.Context, resourceOwner string, policy *domain.PasswordLockoutPolicy) (*domain.PasswordLockoutPolicy, error) {
|
||||
func (c *Commands) ChangeLockoutPolicy(ctx context.Context, resourceOwner string, policy *domain.LockoutPolicy) (*domain.LockoutPolicy, error) {
|
||||
if resourceOwner == "" {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-3J9fs", "Errors.ResourceOwnerMissing")
|
||||
}
|
||||
existingPolicy := NewOrgPasswordLockoutPolicyWriteModel(resourceOwner)
|
||||
existingPolicy := NewOrgLockoutPolicyWriteModel(resourceOwner)
|
||||
err := c.eventstore.FilterToQueryReducer(ctx, existingPolicy)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
|
||||
return nil, caos_errs.ThrowNotFound(nil, "ORG-ADfs1", "Errors.Org.PasswordLockoutPolicy.NotFound")
|
||||
return nil, caos_errs.ThrowNotFound(nil, "ORG-ADfs1", "Errors.Org.LockoutPolicy.NotFound")
|
||||
}
|
||||
|
||||
orgAgg := OrgAggregateFromWriteModel(&existingPolicy.PasswordLockoutPolicyWriteModel.WriteModel)
|
||||
changedEvent, hasChanged := existingPolicy.NewChangedEvent(ctx, orgAgg, policy.MaxAttempts, policy.ShowLockOutFailures)
|
||||
orgAgg := OrgAggregateFromWriteModel(&existingPolicy.LockoutPolicyWriteModel.WriteModel)
|
||||
changedEvent, hasChanged := existingPolicy.NewChangedEvent(ctx, orgAgg, policy.MaxPasswordAttempts, policy.ShowLockOutFailures)
|
||||
if !hasChanged {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "ORG-4M9vs", "Errors.Org.PasswordLockoutPolicy.NotChanged")
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "ORG-4M9vs", "Errors.Org.LockoutPolicy.NotChanged")
|
||||
}
|
||||
|
||||
pushedEvents, err := c.eventstore.PushEvents(ctx, changedEvent)
|
||||
@@ -59,23 +59,23 @@ func (c *Commands) ChangePasswordLockoutPolicy(ctx context.Context, resourceOwne
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return writeModelToPasswordLockoutPolicy(&existingPolicy.PasswordLockoutPolicyWriteModel), nil
|
||||
return writeModelToLockoutPolicy(&existingPolicy.LockoutPolicyWriteModel), nil
|
||||
}
|
||||
|
||||
func (c *Commands) RemovePasswordLockoutPolicy(ctx context.Context, orgID string) error {
|
||||
func (c *Commands) RemoveLockoutPolicy(ctx context.Context, orgID string) error {
|
||||
if orgID == "" {
|
||||
return caos_errs.ThrowInvalidArgument(nil, "Org-4J9fs", "Errors.ResourceOwnerMissing")
|
||||
}
|
||||
existingPolicy := NewOrgPasswordLockoutPolicyWriteModel(orgID)
|
||||
existingPolicy := NewOrgLockoutPolicyWriteModel(orgID)
|
||||
err := c.eventstore.FilterToQueryReducer(ctx, existingPolicy)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
|
||||
return caos_errs.ThrowNotFound(nil, "ORG-D4zuz", "Errors.Org.PasswordLockoutPolicy.NotFound")
|
||||
return caos_errs.ThrowNotFound(nil, "ORG-D4zuz", "Errors.Org.LockoutPolicy.NotFound")
|
||||
}
|
||||
orgAgg := OrgAggregateFromWriteModel(&existingPolicy.WriteModel)
|
||||
|
||||
_, err = c.eventstore.PushEvents(ctx, org.NewPasswordLockoutPolicyRemovedEvent(ctx, orgAgg))
|
||||
_, err = c.eventstore.PushEvents(ctx, org.NewLockoutPolicyRemovedEvent(ctx, orgAgg))
|
||||
return err
|
||||
}
|
||||
|
@@ -9,13 +9,13 @@ import (
|
||||
"github.com/caos/zitadel/internal/repository/policy"
|
||||
)
|
||||
|
||||
type OrgPasswordLockoutPolicyWriteModel struct {
|
||||
PasswordLockoutPolicyWriteModel
|
||||
type OrgLockoutPolicyWriteModel struct {
|
||||
LockoutPolicyWriteModel
|
||||
}
|
||||
|
||||
func NewOrgPasswordLockoutPolicyWriteModel(orgID string) *OrgPasswordLockoutPolicyWriteModel {
|
||||
return &OrgPasswordLockoutPolicyWriteModel{
|
||||
PasswordLockoutPolicyWriteModel{
|
||||
func NewOrgLockoutPolicyWriteModel(orgID string) *OrgLockoutPolicyWriteModel {
|
||||
return &OrgLockoutPolicyWriteModel{
|
||||
LockoutPolicyWriteModel{
|
||||
WriteModel: eventstore.WriteModel{
|
||||
AggregateID: orgID,
|
||||
ResourceOwner: orgID,
|
||||
@@ -24,42 +24,42 @@ func NewOrgPasswordLockoutPolicyWriteModel(orgID string) *OrgPasswordLockoutPoli
|
||||
}
|
||||
}
|
||||
|
||||
func (wm *OrgPasswordLockoutPolicyWriteModel) AppendEvents(events ...eventstore.EventReader) {
|
||||
func (wm *OrgLockoutPolicyWriteModel) AppendEvents(events ...eventstore.EventReader) {
|
||||
for _, event := range events {
|
||||
switch e := event.(type) {
|
||||
case *org.PasswordLockoutPolicyAddedEvent:
|
||||
wm.PasswordLockoutPolicyWriteModel.AppendEvents(&e.PasswordLockoutPolicyAddedEvent)
|
||||
case *org.PasswordLockoutPolicyChangedEvent:
|
||||
wm.PasswordLockoutPolicyWriteModel.AppendEvents(&e.PasswordLockoutPolicyChangedEvent)
|
||||
case *org.PasswordLockoutPolicyRemovedEvent:
|
||||
wm.PasswordLockoutPolicyWriteModel.AppendEvents(&e.PasswordLockoutPolicyRemovedEvent)
|
||||
case *org.LockoutPolicyAddedEvent:
|
||||
wm.LockoutPolicyWriteModel.AppendEvents(&e.LockoutPolicyAddedEvent)
|
||||
case *org.LockoutPolicyChangedEvent:
|
||||
wm.LockoutPolicyWriteModel.AppendEvents(&e.LockoutPolicyChangedEvent)
|
||||
case *org.LockoutPolicyRemovedEvent:
|
||||
wm.LockoutPolicyWriteModel.AppendEvents(&e.LockoutPolicyRemovedEvent)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (wm *OrgPasswordLockoutPolicyWriteModel) Reduce() error {
|
||||
return wm.PasswordLockoutPolicyWriteModel.Reduce()
|
||||
func (wm *OrgLockoutPolicyWriteModel) Reduce() error {
|
||||
return wm.LockoutPolicyWriteModel.Reduce()
|
||||
}
|
||||
|
||||
func (wm *OrgPasswordLockoutPolicyWriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||
func (wm *OrgLockoutPolicyWriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent).
|
||||
ResourceOwner(wm.ResourceOwner).
|
||||
AddQuery().
|
||||
AggregateTypes(org.AggregateType).
|
||||
AggregateIDs(wm.PasswordLockoutPolicyWriteModel.AggregateID).
|
||||
EventTypes(org.PasswordLockoutPolicyAddedEventType,
|
||||
org.PasswordLockoutPolicyChangedEventType,
|
||||
org.PasswordLockoutPolicyRemovedEventType).
|
||||
AggregateIDs(wm.LockoutPolicyWriteModel.AggregateID).
|
||||
EventTypes(org.LockoutPolicyAddedEventType,
|
||||
org.LockoutPolicyChangedEventType,
|
||||
org.LockoutPolicyRemovedEventType).
|
||||
Builder()
|
||||
}
|
||||
|
||||
func (wm *OrgPasswordLockoutPolicyWriteModel) NewChangedEvent(
|
||||
func (wm *OrgLockoutPolicyWriteModel) NewChangedEvent(
|
||||
ctx context.Context,
|
||||
aggregate *eventstore.Aggregate,
|
||||
maxAttempts uint64,
|
||||
showLockoutFailure bool) (*org.PasswordLockoutPolicyChangedEvent, bool) {
|
||||
changes := make([]policy.PasswordLockoutPolicyChanges, 0)
|
||||
if wm.MaxAttempts != maxAttempts {
|
||||
showLockoutFailure bool) (*org.LockoutPolicyChangedEvent, bool) {
|
||||
changes := make([]policy.LockoutPolicyChanges, 0)
|
||||
if wm.MaxPasswordAttempts != maxAttempts {
|
||||
changes = append(changes, policy.ChangeMaxAttempts(maxAttempts))
|
||||
}
|
||||
if wm.ShowLockOutFailures != showLockoutFailure {
|
||||
@@ -68,7 +68,7 @@ func (wm *OrgPasswordLockoutPolicyWriteModel) NewChangedEvent(
|
||||
if len(changes) == 0 {
|
||||
return nil, false
|
||||
}
|
||||
changedEvent, err := org.NewPasswordLockoutPolicyChangedEvent(ctx, aggregate, changes)
|
||||
changedEvent, err := org.NewLockoutPolicyChangedEvent(ctx, aggregate, changes)
|
||||
if err != nil {
|
||||
return nil, false
|
||||
}
|
||||
|
@@ -22,10 +22,10 @@ func TestCommandSide_AddPasswordLockoutPolicy(t *testing.T) {
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
orgID string
|
||||
policy *domain.PasswordLockoutPolicy
|
||||
policy *domain.LockoutPolicy
|
||||
}
|
||||
type res struct {
|
||||
want *domain.PasswordLockoutPolicy
|
||||
want *domain.LockoutPolicy
|
||||
err func(error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
@@ -43,8 +43,8 @@ func TestCommandSide_AddPasswordLockoutPolicy(t *testing.T) {
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
policy: &domain.PasswordLockoutPolicy{
|
||||
MaxAttempts: 10,
|
||||
policy: &domain.LockoutPolicy{
|
||||
MaxPasswordAttempts: 10,
|
||||
ShowLockOutFailures: true,
|
||||
},
|
||||
},
|
||||
@@ -59,7 +59,7 @@ func TestCommandSide_AddPasswordLockoutPolicy(t *testing.T) {
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewPasswordLockoutPolicyAddedEvent(context.Background(),
|
||||
org.NewLockoutPolicyAddedEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
10,
|
||||
true,
|
||||
@@ -71,8 +71,8 @@ func TestCommandSide_AddPasswordLockoutPolicy(t *testing.T) {
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
orgID: "org1",
|
||||
policy: &domain.PasswordLockoutPolicy{
|
||||
MaxAttempts: 10,
|
||||
policy: &domain.LockoutPolicy{
|
||||
MaxPasswordAttempts: 10,
|
||||
ShowLockOutFailures: true,
|
||||
},
|
||||
},
|
||||
@@ -89,7 +89,7 @@ func TestCommandSide_AddPasswordLockoutPolicy(t *testing.T) {
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(
|
||||
org.NewPasswordLockoutPolicyAddedEvent(context.Background(),
|
||||
org.NewLockoutPolicyAddedEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
10,
|
||||
true,
|
||||
@@ -102,18 +102,18 @@ func TestCommandSide_AddPasswordLockoutPolicy(t *testing.T) {
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
orgID: "org1",
|
||||
policy: &domain.PasswordLockoutPolicy{
|
||||
MaxAttempts: 10,
|
||||
policy: &domain.LockoutPolicy{
|
||||
MaxPasswordAttempts: 10,
|
||||
ShowLockOutFailures: true,
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
want: &domain.PasswordLockoutPolicy{
|
||||
want: &domain.LockoutPolicy{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "org1",
|
||||
ResourceOwner: "org1",
|
||||
},
|
||||
MaxAttempts: 10,
|
||||
MaxPasswordAttempts: 10,
|
||||
ShowLockOutFailures: true,
|
||||
},
|
||||
},
|
||||
@@ -124,7 +124,7 @@ func TestCommandSide_AddPasswordLockoutPolicy(t *testing.T) {
|
||||
r := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
}
|
||||
got, err := r.AddPasswordLockoutPolicy(tt.args.ctx, tt.args.orgID, tt.args.policy)
|
||||
got, err := r.AddLockoutPolicy(tt.args.ctx, tt.args.orgID, tt.args.policy)
|
||||
if tt.res.err == nil {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
@@ -145,10 +145,10 @@ func TestCommandSide_ChangePasswordLockoutPolicy(t *testing.T) {
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
orgID string
|
||||
policy *domain.PasswordLockoutPolicy
|
||||
policy *domain.LockoutPolicy
|
||||
}
|
||||
type res struct {
|
||||
want *domain.PasswordLockoutPolicy
|
||||
want *domain.LockoutPolicy
|
||||
err func(error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
@@ -166,8 +166,8 @@ func TestCommandSide_ChangePasswordLockoutPolicy(t *testing.T) {
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
policy: &domain.PasswordLockoutPolicy{
|
||||
MaxAttempts: 10,
|
||||
policy: &domain.LockoutPolicy{
|
||||
MaxPasswordAttempts: 10,
|
||||
ShowLockOutFailures: true,
|
||||
},
|
||||
},
|
||||
@@ -186,8 +186,8 @@ func TestCommandSide_ChangePasswordLockoutPolicy(t *testing.T) {
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
orgID: "org1",
|
||||
policy: &domain.PasswordLockoutPolicy{
|
||||
MaxAttempts: 10,
|
||||
policy: &domain.LockoutPolicy{
|
||||
MaxPasswordAttempts: 10,
|
||||
ShowLockOutFailures: true,
|
||||
},
|
||||
},
|
||||
@@ -202,7 +202,7 @@ func TestCommandSide_ChangePasswordLockoutPolicy(t *testing.T) {
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewPasswordLockoutPolicyAddedEvent(context.Background(),
|
||||
org.NewLockoutPolicyAddedEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
10,
|
||||
true,
|
||||
@@ -214,8 +214,8 @@ func TestCommandSide_ChangePasswordLockoutPolicy(t *testing.T) {
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
orgID: "org1",
|
||||
policy: &domain.PasswordLockoutPolicy{
|
||||
MaxAttempts: 10,
|
||||
policy: &domain.LockoutPolicy{
|
||||
MaxPasswordAttempts: 10,
|
||||
ShowLockOutFailures: true,
|
||||
},
|
||||
},
|
||||
@@ -230,7 +230,7 @@ func TestCommandSide_ChangePasswordLockoutPolicy(t *testing.T) {
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewPasswordLockoutPolicyAddedEvent(context.Background(),
|
||||
org.NewLockoutPolicyAddedEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
10,
|
||||
true,
|
||||
@@ -249,18 +249,18 @@ func TestCommandSide_ChangePasswordLockoutPolicy(t *testing.T) {
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
orgID: "org1",
|
||||
policy: &domain.PasswordLockoutPolicy{
|
||||
MaxAttempts: 5,
|
||||
policy: &domain.LockoutPolicy{
|
||||
MaxPasswordAttempts: 5,
|
||||
ShowLockOutFailures: false,
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
want: &domain.PasswordLockoutPolicy{
|
||||
want: &domain.LockoutPolicy{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: "org1",
|
||||
ResourceOwner: "org1",
|
||||
},
|
||||
MaxAttempts: 5,
|
||||
MaxPasswordAttempts: 5,
|
||||
ShowLockOutFailures: false,
|
||||
},
|
||||
},
|
||||
@@ -271,7 +271,7 @@ func TestCommandSide_ChangePasswordLockoutPolicy(t *testing.T) {
|
||||
r := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
}
|
||||
got, err := r.ChangePasswordLockoutPolicy(tt.args.ctx, tt.args.orgID, tt.args.policy)
|
||||
got, err := r.ChangeLockoutPolicy(tt.args.ctx, tt.args.orgID, tt.args.policy)
|
||||
if tt.res.err == nil {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
@@ -340,7 +340,7 @@ func TestCommandSide_RemovePasswordLockoutPolicy(t *testing.T) {
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewPasswordLockoutPolicyAddedEvent(context.Background(),
|
||||
org.NewLockoutPolicyAddedEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
10,
|
||||
true,
|
||||
@@ -350,7 +350,7 @@ func TestCommandSide_RemovePasswordLockoutPolicy(t *testing.T) {
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(
|
||||
org.NewPasswordLockoutPolicyRemovedEvent(context.Background(),
|
||||
org.NewLockoutPolicyRemovedEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate),
|
||||
),
|
||||
},
|
||||
@@ -373,7 +373,7 @@ func TestCommandSide_RemovePasswordLockoutPolicy(t *testing.T) {
|
||||
r := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
}
|
||||
err := r.RemovePasswordLockoutPolicy(tt.args.ctx, tt.args.orgID)
|
||||
err := r.RemoveLockoutPolicy(tt.args.ctx, tt.args.orgID)
|
||||
if tt.res.err == nil {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
@@ -384,10 +384,10 @@ func TestCommandSide_RemovePasswordLockoutPolicy(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func newPasswordLockoutPolicyChangedEvent(ctx context.Context, orgID string, maxAttempts uint64, showLockoutFailure bool) *org.PasswordLockoutPolicyChangedEvent {
|
||||
event, _ := org.NewPasswordLockoutPolicyChangedEvent(ctx,
|
||||
func newPasswordLockoutPolicyChangedEvent(ctx context.Context, orgID string, maxAttempts uint64, showLockoutFailure bool) *org.LockoutPolicyChangedEvent {
|
||||
event, _ := org.NewLockoutPolicyChangedEvent(ctx,
|
||||
&org.NewAggregate(orgID, orgID).Aggregate,
|
||||
[]policy.PasswordLockoutPolicyChanges{
|
||||
[]policy.LockoutPolicyChanges{
|
||||
policy.ChangeMaxAttempts(maxAttempts),
|
||||
policy.ChangeShowLockOutFailures(showLockoutFailure),
|
||||
},
|
||||
|
@@ -6,29 +6,29 @@ import (
|
||||
"github.com/caos/zitadel/internal/repository/policy"
|
||||
)
|
||||
|
||||
type PasswordLockoutPolicyWriteModel struct {
|
||||
type LockoutPolicyWriteModel struct {
|
||||
eventstore.WriteModel
|
||||
|
||||
MaxAttempts uint64
|
||||
MaxPasswordAttempts uint64
|
||||
ShowLockOutFailures bool
|
||||
State domain.PolicyState
|
||||
}
|
||||
|
||||
func (wm *PasswordLockoutPolicyWriteModel) Reduce() error {
|
||||
func (wm *LockoutPolicyWriteModel) Reduce() error {
|
||||
for _, event := range wm.Events {
|
||||
switch e := event.(type) {
|
||||
case *policy.PasswordLockoutPolicyAddedEvent:
|
||||
wm.MaxAttempts = e.MaxAttempts
|
||||
case *policy.LockoutPolicyAddedEvent:
|
||||
wm.MaxPasswordAttempts = e.MaxPasswordAttempts
|
||||
wm.ShowLockOutFailures = e.ShowLockOutFailures
|
||||
wm.State = domain.PolicyStateActive
|
||||
case *policy.PasswordLockoutPolicyChangedEvent:
|
||||
if e.MaxAttempts != nil {
|
||||
wm.MaxAttempts = *e.MaxAttempts
|
||||
case *policy.LockoutPolicyChangedEvent:
|
||||
if e.MaxPasswordAttempts != nil {
|
||||
wm.MaxPasswordAttempts = *e.MaxPasswordAttempts
|
||||
}
|
||||
if e.ShowLockOutFailures != nil {
|
||||
wm.ShowLockOutFailures = *e.ShowLockOutFailures
|
||||
}
|
||||
case *policy.PasswordLockoutPolicyRemovedEvent:
|
||||
case *policy.LockoutPolicyRemovedEvent:
|
||||
wm.State = domain.PolicyStateRemoved
|
||||
}
|
||||
}
|
||||
|
35
internal/command/setup_step18.go
Normal file
35
internal/command/setup_step18.go
Normal file
@@ -0,0 +1,35 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/caos/logging"
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
)
|
||||
|
||||
type Step18 struct {
|
||||
LockoutPolicy domain.LockoutPolicy
|
||||
}
|
||||
|
||||
func (s *Step18) Step() domain.Step {
|
||||
return domain.Step18
|
||||
}
|
||||
|
||||
func (s *Step18) execute(ctx context.Context, commandSide *Commands) error {
|
||||
return commandSide.SetupStep18(ctx, s)
|
||||
}
|
||||
|
||||
func (c *Commands) SetupStep18(ctx context.Context, step *Step18) error {
|
||||
fn := func(iam *IAMWriteModel) ([]eventstore.EventPusher, error) {
|
||||
iamAgg := IAMAggregateFromWriteModel(&iam.WriteModel)
|
||||
addedPolicy := NewIAMLockoutPolicyWriteModel()
|
||||
events, err := c.addDefaultLockoutPolicy(ctx, iamAgg, addedPolicy, &step.LockoutPolicy)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
logging.Log("SETUP-3m99ds").Info("default lockout policy set up")
|
||||
return []eventstore.EventPusher{events}, nil
|
||||
}
|
||||
return c.setup(ctx, step, fn)
|
||||
}
|
@@ -3,15 +3,13 @@ package command
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/caos/logging"
|
||||
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
)
|
||||
|
||||
type Step4 struct {
|
||||
DefaultPasswordLockoutPolicy domain.PasswordLockoutPolicy
|
||||
DefaultPasswordLockoutPolicy domain.LockoutPolicy
|
||||
}
|
||||
|
||||
func (s *Step4) Step() domain.Step {
|
||||
@@ -21,19 +19,12 @@ func (s *Step4) Step() domain.Step {
|
||||
func (s *Step4) execute(ctx context.Context, commandSide *Commands) error {
|
||||
return commandSide.SetupStep4(ctx, s)
|
||||
}
|
||||
|
||||
//This step should not be executed when a new instance is setup, because its not used anymore
|
||||
//SetupStep4 is no op in favour of step 18.
|
||||
//Password lockout policy is replaced by lockout policy
|
||||
func (c *Commands) SetupStep4(ctx context.Context, step *Step4) error {
|
||||
fn := func(iam *IAMWriteModel) ([]eventstore.EventPusher, error) {
|
||||
iamAgg := IAMAggregateFromWriteModel(&iam.WriteModel)
|
||||
event, err := c.addDefaultPasswordLockoutPolicy(ctx, iamAgg, NewIAMPasswordLockoutPolicyWriteModel(), &domain.PasswordLockoutPolicy{
|
||||
MaxAttempts: step.DefaultPasswordLockoutPolicy.MaxAttempts,
|
||||
ShowLockOutFailures: step.DefaultPasswordLockoutPolicy.ShowLockOutFailures,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
logging.Log("SETUP-Bfnge").Info("default password lockout policy set up")
|
||||
return []eventstore.EventPusher{event}, nil
|
||||
return nil, nil
|
||||
}
|
||||
return c.setup(ctx, step, fn)
|
||||
}
|
||||
|
@@ -194,7 +194,7 @@ func (c *Commands) PasswordCodeSent(ctx context.Context, orgID, userID string) (
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *Commands) HumanCheckPassword(ctx context.Context, orgID, userID, password string, authRequest *domain.AuthRequest) (err error) {
|
||||
func (c *Commands) HumanCheckPassword(ctx context.Context, orgID, userID, password string, authRequest *domain.AuthRequest, lockoutPolicy *domain.LockoutPolicy) (err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
@@ -225,7 +225,15 @@ func (c *Commands) HumanCheckPassword(ctx context.Context, orgID, userID, passwo
|
||||
_, err = c.eventstore.PushEvents(ctx, user.NewHumanPasswordCheckSucceededEvent(ctx, userAgg, authRequestDomainToAuthRequestInfo(authRequest)))
|
||||
return err
|
||||
}
|
||||
_, err = c.eventstore.PushEvents(ctx, user.NewHumanPasswordCheckFailedEvent(ctx, userAgg, authRequestDomainToAuthRequestInfo(authRequest)))
|
||||
events := make([]eventstore.EventPusher, 0)
|
||||
events = append(events, user.NewHumanPasswordCheckFailedEvent(ctx, userAgg, authRequestDomainToAuthRequestInfo(authRequest)))
|
||||
if lockoutPolicy != nil && lockoutPolicy.MaxPasswordAttempts > 0 {
|
||||
if existingPassword.PasswordCheckFailedCount+1 >= lockoutPolicy.MaxPasswordAttempts {
|
||||
events = append(events, user.NewUserLockedEvent(ctx, userAgg))
|
||||
}
|
||||
|
||||
}
|
||||
_, err = c.eventstore.PushEvents(ctx, events...)
|
||||
logging.Log("COMMAND-9fj7s").OnError(err).Error("error create password check failed event")
|
||||
return caos_errs.ThrowInvalidArgument(nil, "COMMAND-452ad", "Errors.User.Password.Invalid")
|
||||
}
|
||||
|
@@ -16,9 +16,10 @@ type HumanPasswordWriteModel struct {
|
||||
Secret *crypto.CryptoValue
|
||||
SecretChangeRequired bool
|
||||
|
||||
Code *crypto.CryptoValue
|
||||
CodeCreationDate time.Time
|
||||
CodeExpiry time.Duration
|
||||
Code *crypto.CryptoValue
|
||||
CodeCreationDate time.Time
|
||||
CodeExpiry time.Duration
|
||||
PasswordCheckFailedCount uint64
|
||||
|
||||
UserState domain.UserState
|
||||
}
|
||||
@@ -51,6 +52,7 @@ func (wm *HumanPasswordWriteModel) Reduce() error {
|
||||
wm.Secret = e.Secret
|
||||
wm.SecretChangeRequired = e.ChangeRequired
|
||||
wm.Code = nil
|
||||
wm.PasswordCheckFailedCount = 0
|
||||
case *user.HumanPasswordCodeAddedEvent:
|
||||
wm.Code = e.Code
|
||||
wm.CodeCreationDate = e.CreationDate()
|
||||
@@ -59,6 +61,12 @@ func (wm *HumanPasswordWriteModel) Reduce() error {
|
||||
if wm.UserState == domain.UserStateInitial {
|
||||
wm.UserState = domain.UserStateActive
|
||||
}
|
||||
case *user.HumanPasswordCheckFailedEvent:
|
||||
wm.PasswordCheckFailedCount += 1
|
||||
case *user.HumanPasswordCheckSucceededEvent:
|
||||
wm.PasswordCheckFailedCount = 0
|
||||
case *user.UserUnlockedEvent:
|
||||
wm.PasswordCheckFailedCount = 0
|
||||
case *user.UserRemovedEvent:
|
||||
wm.UserState = domain.UserStateDeleted
|
||||
}
|
||||
@@ -78,14 +86,19 @@ func (wm *HumanPasswordWriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||
user.HumanPasswordChangedType,
|
||||
user.HumanPasswordCodeAddedType,
|
||||
user.HumanEmailVerifiedType,
|
||||
user.HumanPasswordCheckFailedType,
|
||||
user.HumanPasswordCheckSucceededType,
|
||||
user.UserRemovedType,
|
||||
user.UserUnlockedType,
|
||||
user.UserV1AddedType,
|
||||
user.UserV1RegisteredType,
|
||||
user.UserV1InitialCodeAddedType,
|
||||
user.UserV1InitializedCheckSucceededType,
|
||||
user.UserV1PasswordChangedType,
|
||||
user.UserV1PasswordCodeAddedType,
|
||||
user.UserV1EmailVerifiedType).
|
||||
user.UserV1EmailVerifiedType,
|
||||
user.UserV1PasswordCheckFailedType,
|
||||
user.UserV1PasswordCheckSucceededType).
|
||||
Builder()
|
||||
|
||||
if wm.ResourceOwner != "" {
|
||||
|
@@ -1082,6 +1082,7 @@ func TestCommandSide_CheckPassword(t *testing.T) {
|
||||
resourceOwner string
|
||||
password string
|
||||
authReq *domain.AuthRequest
|
||||
lockoutPolicy *domain.LockoutPolicy
|
||||
}
|
||||
type res struct {
|
||||
err func(error) bool
|
||||
@@ -1177,7 +1178,7 @@ func TestCommandSide_CheckPassword(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "password not matching, precondition error",
|
||||
name: "password not matching lockout policy not relevant, precondition error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
@@ -1238,6 +1239,82 @@ func TestCommandSide_CheckPassword(t *testing.T) {
|
||||
ID: "request1",
|
||||
AgentID: "agent1",
|
||||
},
|
||||
lockoutPolicy: &domain.LockoutPolicy{},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "password not matching, max password attempts reached - user locked, precondition error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
user.NewHumanAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
"username",
|
||||
"firstname",
|
||||
"lastname",
|
||||
"nickname",
|
||||
"displayname",
|
||||
language.German,
|
||||
domain.GenderUnspecified,
|
||||
"email@test.ch",
|
||||
true,
|
||||
),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
user.NewHumanEmailVerifiedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
user.NewHumanPasswordChangedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeHash,
|
||||
Algorithm: "hash",
|
||||
KeyID: "",
|
||||
Crypted: []byte("password"),
|
||||
},
|
||||
false,
|
||||
"")),
|
||||
),
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusher(
|
||||
user.NewHumanPasswordCheckFailedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
&user.AuthRequestInfo{
|
||||
ID: "request1",
|
||||
UserAgentID: "agent1",
|
||||
},
|
||||
),
|
||||
),
|
||||
eventFromEventPusher(
|
||||
user.NewUserLockedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
),
|
||||
),
|
||||
},
|
||||
),
|
||||
),
|
||||
userPasswordAlg: crypto.CreateMockHashAlg(gomock.NewController(t)),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
userID: "user1",
|
||||
password: "password1",
|
||||
resourceOwner: "org1",
|
||||
authReq: &domain.AuthRequest{
|
||||
ID: "request1",
|
||||
AgentID: "agent1",
|
||||
},
|
||||
lockoutPolicy: &domain.LockoutPolicy{
|
||||
MaxPasswordAttempts: 1,
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
@@ -1315,7 +1392,7 @@ func TestCommandSide_CheckPassword(t *testing.T) {
|
||||
eventstore: tt.fields.eventstore,
|
||||
userPasswordAlg: tt.fields.userPasswordAlg,
|
||||
}
|
||||
err := r.HumanCheckPassword(tt.args.ctx, tt.args.resourceOwner, tt.args.userID, tt.args.password, tt.args.authReq)
|
||||
err := r.HumanCheckPassword(tt.args.ctx, tt.args.resourceOwner, tt.args.userID, tt.args.password, tt.args.authReq, tt.args.lockoutPolicy)
|
||||
if tt.res.err == nil {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
Reference in New Issue
Block a user