feat: add secret generators for OTP (#6262)

This PR adds configuration options for OTP codes through Admin API.
This commit is contained in:
Livio Spring
2023-07-26 13:00:41 +02:00
committed by GitHub
parent 2241c82134
commit 2fe76acd14
22 changed files with 199 additions and 110 deletions

View File

@@ -48,6 +48,8 @@ type InstanceSetup struct {
PasswordVerificationCode *crypto.GeneratorConfig
PasswordlessInitCode *crypto.GeneratorConfig
DomainVerification *crypto.GeneratorConfig
OTPSMS *crypto.GeneratorConfig
OTPEmail *crypto.GeneratorConfig
}
PasswordComplexityPolicy struct {
MinLength uint64
@@ -201,6 +203,8 @@ func (c *Commands) SetUpInstance(ctx context.Context, setup *InstanceSetup) (str
prepareAddSecretGeneratorConfig(instanceAgg, domain.SecretGeneratorTypePasswordResetCode, setup.SecretGenerators.PasswordVerificationCode),
prepareAddSecretGeneratorConfig(instanceAgg, domain.SecretGeneratorTypePasswordlessInitCode, setup.SecretGenerators.PasswordlessInitCode),
prepareAddSecretGeneratorConfig(instanceAgg, domain.SecretGeneratorTypeVerifyDomain, setup.SecretGenerators.DomainVerification),
prepareAddSecretGeneratorConfig(instanceAgg, domain.SecretGeneratorTypeOTPSMS, setup.SecretGenerators.OTPSMS),
prepareAddSecretGeneratorConfig(instanceAgg, domain.SecretGeneratorTypeOTPEmail, setup.SecretGenerators.OTPEmail),
prepareAddDefaultPasswordComplexityPolicy(
instanceAgg,

View File

@@ -79,10 +79,26 @@ func (c *Commands) ChangeSecretGeneratorConfig(ctx context.Context, generatorTyp
if err != nil {
return nil, err
}
if generatorWriteModel.State == domain.SecretGeneratorStateUnspecified || generatorWriteModel.State == domain.SecretGeneratorStateRemoved {
return nil, errors.ThrowNotFound(nil, "COMMAND-3n9ls", "Errors.SecretGenerator.NotFound")
}
instanceAgg := InstanceAggregateFromWriteModel(&generatorWriteModel.WriteModel)
if generatorWriteModel.State == domain.SecretGeneratorStateUnspecified || generatorWriteModel.State == domain.SecretGeneratorStateRemoved {
err = c.pushAppendAndReduce(ctx, generatorWriteModel,
instance.NewSecretGeneratorAddedEvent(
ctx,
instanceAgg,
generatorType,
config.Length,
config.Expiry,
config.IncludeLowerLetters,
config.IncludeUpperLetters,
config.IncludeDigits,
config.IncludeSymbols,
),
)
if err != nil {
return nil, err
}
return writeModelToObjectDetails(&generatorWriteModel.WriteModel), nil
}
changedEvent, hasChanged, err := generatorWriteModel.NewChangedEvent(
ctx,
@@ -100,12 +116,7 @@ func (c *Commands) ChangeSecretGeneratorConfig(ctx context.Context, generatorTyp
if !hasChanged {
return nil, errors.ThrowPreconditionFailed(nil, "COMMAND-m0o3f", "Errors.NoChangesFound")
}
pushedEvents, err := c.eventstore.Push(ctx, changedEvent)
if err != nil {
return nil, err
}
err = AppendAndReduce(generatorWriteModel, pushedEvents...)
if err != nil {
if err = c.pushAppendAndReduce(ctx, generatorWriteModel, changedEvent); err != nil {
return nil, err
}
return writeModelToObjectDetails(&generatorWriteModel.WriteModel), nil

View File

@@ -6,8 +6,8 @@ import (
"time"
"github.com/stretchr/testify/assert"
"github.com/zitadel/zitadel/internal/api/authz"
"github.com/zitadel/zitadel/internal/api/authz"
"github.com/zitadel/zitadel/internal/crypto"
"github.com/zitadel/zitadel/internal/domain"
caos_errs "github.com/zitadel/zitadel/internal/errors"
@@ -155,7 +155,7 @@ func TestCommandSide_AddSecretGenerator(t *testing.T) {
func TestCommandSide_ChangeSecretGenerator(t *testing.T) {
type fields struct {
eventstore *eventstore.Eventstore
eventstore func(t *testing.T) *eventstore.Eventstore
}
type args struct {
ctx context.Context
@@ -176,12 +176,10 @@ func TestCommandSide_ChangeSecretGenerator(t *testing.T) {
{
name: "empty generatortype, invalid error",
fields: fields{
eventstore: eventstoreExpect(
t,
),
eventstore: expectEventstore(),
},
args: args{
ctx: context.Background(),
ctx: authz.WithInstanceID(context.Background(), "INSTANCE"),
generator: &crypto.GeneratorConfig{},
generatorType: domain.SecretGeneratorTypeUnspecified,
},
@@ -190,26 +188,53 @@ func TestCommandSide_ChangeSecretGenerator(t *testing.T) {
},
},
{
name: "generator not existing, not found error",
name: "generator not existing, new added ok",
fields: fields{
eventstore: eventstoreExpect(
t,
eventstore: expectEventstore(
expectFilter(),
expectPush(
[]*repository.Event{
eventFromEventPusherWithInstanceID(
"INSTANCE",
instance.NewSecretGeneratorAddedEvent(
context.Background(),
&instance.NewAggregate("INSTANCE").Aggregate,
domain.SecretGeneratorTypeInitCode,
4,
time.Hour*1,
true,
true,
true,
true,
),
),
},
uniqueConstraintsFromEventConstraintWithInstanceID("INSTANCE", instance.NewAddSecretGeneratorTypeUniqueConstraint(domain.SecretGeneratorTypeInitCode)),
),
),
},
args: args{
ctx: context.Background(),
ctx: authz.WithInstanceID(context.Background(), "INSTANCE"),
generator: &crypto.GeneratorConfig{
Length: 4,
Expiry: 1 * time.Hour,
IncludeLowerLetters: true,
IncludeUpperLetters: true,
IncludeDigits: true,
IncludeSymbols: true,
},
generatorType: domain.SecretGeneratorTypeInitCode,
},
res: res{
err: caos_errs.IsNotFound,
want: &domain.ObjectDetails{
ResourceOwner: "INSTANCE",
},
},
},
{
name: "generator removed, not found error",
name: "generator removed, new added ok",
fields: fields{
eventstore: eventstoreExpect(
t,
eventstore: expectEventstore(
expectFilter(
eventFromEventPusher(
instance.NewSecretGeneratorAddedEvent(
@@ -230,21 +255,49 @@ func TestCommandSide_ChangeSecretGenerator(t *testing.T) {
domain.SecretGeneratorTypeInitCode),
),
),
expectPush(
[]*repository.Event{
eventFromEventPusherWithInstanceID(
"INSTANCE",
instance.NewSecretGeneratorAddedEvent(
context.Background(),
&instance.NewAggregate("INSTANCE").Aggregate,
domain.SecretGeneratorTypeInitCode,
4,
time.Hour*1,
true,
true,
true,
true,
),
),
},
uniqueConstraintsFromEventConstraintWithInstanceID("INSTANCE", instance.NewAddSecretGeneratorTypeUniqueConstraint(domain.SecretGeneratorTypeInitCode)),
),
),
},
args: args{
ctx: context.Background(),
ctx: authz.WithInstanceID(context.Background(), "INSTANCE"),
generator: &crypto.GeneratorConfig{
Length: 4,
Expiry: 1 * time.Hour,
IncludeLowerLetters: true,
IncludeUpperLetters: true,
IncludeDigits: true,
IncludeSymbols: true,
},
generatorType: domain.SecretGeneratorTypeInitCode,
},
res: res{
err: caos_errs.IsNotFound,
want: &domain.ObjectDetails{
ResourceOwner: "INSTANCE",
},
},
},
{
name: "no changes, precondition error",
fields: fields{
eventstore: eventstoreExpect(
t,
eventstore: expectEventstore(
expectFilter(
eventFromEventPusher(
instance.NewSecretGeneratorAddedEvent(
@@ -263,7 +316,7 @@ func TestCommandSide_ChangeSecretGenerator(t *testing.T) {
),
},
args: args{
ctx: context.Background(),
ctx: authz.WithInstanceID(context.Background(), "INSTANCE"),
generator: &crypto.GeneratorConfig{
Length: 4,
Expiry: 1 * time.Hour,
@@ -281,8 +334,7 @@ func TestCommandSide_ChangeSecretGenerator(t *testing.T) {
{
name: "secret generator change, ok",
fields: fields{
eventstore: eventstoreExpect(
t,
eventstore: expectEventstore(
expectFilter(
eventFromEventPusher(
instance.NewSecretGeneratorAddedEvent(
@@ -300,7 +352,7 @@ func TestCommandSide_ChangeSecretGenerator(t *testing.T) {
),
expectPush(
[]*repository.Event{
eventFromEventPusher(
eventFromEventPusherWithInstanceID("INSTANCE",
newSecretGeneratorChangedEvent(context.Background(),
domain.SecretGeneratorTypeInitCode,
8,
@@ -308,14 +360,15 @@ func TestCommandSide_ChangeSecretGenerator(t *testing.T) {
false,
false,
false,
false),
false,
),
),
},
),
),
},
args: args{
ctx: context.Background(),
ctx: authz.WithInstanceID(context.Background(), "INSTANCE"),
generator: &crypto.GeneratorConfig{
Length: 8,
Expiry: 2 * time.Hour,
@@ -336,7 +389,7 @@ func TestCommandSide_ChangeSecretGenerator(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := &Commands{
eventstore: tt.fields.eventstore,
eventstore: tt.fields.eventstore(t),
}
got, err := r.ChangeSecretGeneratorConfig(tt.args.ctx, tt.args.generatorType, tt.args.generator)
if tt.res.err == nil {