diff --git a/internal/api/grpc/admin/login_policy.go b/internal/api/grpc/admin/login_policy.go index 65fb52032..2232883c3 100644 --- a/internal/api/grpc/admin/login_policy.go +++ b/internal/api/grpc/admin/login_policy.go @@ -88,7 +88,7 @@ func (s *Server) ListLoginPolicySecondFactors(ctx context.Context, req *admin_pb } func (s *Server) AddSecondFactorToLoginPolicy(ctx context.Context, req *admin_pb.AddSecondFactorToLoginPolicyRequest) (*admin_pb.AddSecondFactorToLoginPolicyResponse, error) { - _, objectDetails, err := s.command.AddSecondFactorToDefaultLoginPolicy(ctx, policy_grpc.SecondFactorTypeToDomain(req.Type)) + objectDetails, err := s.command.AddSecondFactorToDefaultLoginPolicy(ctx, policy_grpc.SecondFactorTypeToDomain(req.Type)) if err != nil { return nil, err } @@ -119,7 +119,7 @@ func (s *Server) ListLoginPolicyMultiFactors(ctx context.Context, req *admin_pb. } func (s *Server) AddMultiFactorToLoginPolicy(ctx context.Context, req *admin_pb.AddMultiFactorToLoginPolicyRequest) (*admin_pb.AddMultiFactorToLoginPolicyResponse, error) { - _, objectDetails, err := s.command.AddMultiFactorToDefaultLoginPolicy(ctx, policy_grpc.MultiFactorTypeToDomain(req.Type)) + objectDetails, err := s.command.AddMultiFactorToDefaultLoginPolicy(ctx, policy_grpc.MultiFactorTypeToDomain(req.Type)) if err != nil { return nil, err } diff --git a/internal/command/converter.go b/internal/command/converter.go index ad734ca40..563500d61 100644 --- a/internal/command/converter.go +++ b/internal/command/converter.go @@ -12,3 +12,11 @@ func writeModelToObjectDetails(writeModel *eventstore.WriteModel) *domain.Object EventDate: writeModel.ChangeDate, } } + +func pushedEventsToObjectDetails(events []eventstore.Event) *domain.ObjectDetails { + return &domain.ObjectDetails{ + Sequence: events[len(events)-1].Sequence(), + EventDate: events[len(events)-1].CreationDate(), + ResourceOwner: events[len(events)-1].Aggregate().ResourceOwner, + } +} diff --git a/internal/command/instance.go b/internal/command/instance.go index 2048a3b5e..ad2fd7cf1 100644 --- a/internal/command/instance.go +++ b/internal/command/instance.go @@ -174,16 +174,16 @@ func (c *Commands) SetUpInstance(ctx context.Context, setup *InstanceSetup) (str projectAgg := project.NewAggregate(setup.zitadel.projectID, orgID) validations := []preparation.Validation{ - addInstance(instanceAgg, setup.InstanceName, setup.DefaultLanguage), - addSecretGeneratorConfig(instanceAgg, domain.SecretGeneratorTypeAppSecret, setup.SecretGenerators.ClientSecret), - addSecretGeneratorConfig(instanceAgg, domain.SecretGeneratorTypeInitCode, setup.SecretGenerators.InitializeUserCode), - addSecretGeneratorConfig(instanceAgg, domain.SecretGeneratorTypeVerifyEmailCode, setup.SecretGenerators.EmailVerificationCode), - addSecretGeneratorConfig(instanceAgg, domain.SecretGeneratorTypeVerifyPhoneCode, setup.SecretGenerators.PhoneVerificationCode), - addSecretGeneratorConfig(instanceAgg, domain.SecretGeneratorTypePasswordResetCode, setup.SecretGenerators.PasswordVerificationCode), - addSecretGeneratorConfig(instanceAgg, domain.SecretGeneratorTypePasswordlessInitCode, setup.SecretGenerators.PasswordlessInitCode), - addSecretGeneratorConfig(instanceAgg, domain.SecretGeneratorTypeVerifyDomain, setup.SecretGenerators.DomainVerification), + prepareAddInstance(instanceAgg, setup.InstanceName, setup.DefaultLanguage), + prepareAddSecretGeneratorConfig(instanceAgg, domain.SecretGeneratorTypeAppSecret, setup.SecretGenerators.ClientSecret), + prepareAddSecretGeneratorConfig(instanceAgg, domain.SecretGeneratorTypeInitCode, setup.SecretGenerators.InitializeUserCode), + prepareAddSecretGeneratorConfig(instanceAgg, domain.SecretGeneratorTypeVerifyEmailCode, setup.SecretGenerators.EmailVerificationCode), + prepareAddSecretGeneratorConfig(instanceAgg, domain.SecretGeneratorTypeVerifyPhoneCode, setup.SecretGenerators.PhoneVerificationCode), + prepareAddSecretGeneratorConfig(instanceAgg, domain.SecretGeneratorTypePasswordResetCode, setup.SecretGenerators.PasswordVerificationCode), + prepareAddSecretGeneratorConfig(instanceAgg, domain.SecretGeneratorTypePasswordlessInitCode, setup.SecretGenerators.PasswordlessInitCode), + prepareAddSecretGeneratorConfig(instanceAgg, domain.SecretGeneratorTypeVerifyDomain, setup.SecretGenerators.DomainVerification), - AddPasswordComplexityPolicy( + prepareAddDefaultPasswordComplexityPolicy( instanceAgg, setup.PasswordComplexityPolicy.MinLength, setup.PasswordComplexityPolicy.HasLowercase, @@ -191,18 +191,18 @@ func (c *Commands) SetUpInstance(ctx context.Context, setup *InstanceSetup) (str setup.PasswordComplexityPolicy.HasNumber, setup.PasswordComplexityPolicy.HasSymbol, ), - AddPasswordAgePolicy( + prepareAddDefaultPasswordAgePolicy( instanceAgg, setup.PasswordAgePolicy.ExpireWarnDays, setup.PasswordAgePolicy.MaxAgeDays, ), - AddDefaultDomainPolicy( + prepareAddDefaultDomainPolicy( instanceAgg, setup.DomainPolicy.UserLoginMustBeDomain, setup.DomainPolicy.ValidateOrgDomains, setup.DomainPolicy.SMTPSenderAddressMatchesInstanceDomain, ), - AddDefaultLoginPolicy( + prepareAddDefaultLoginPolicy( instanceAgg, setup.LoginPolicy.AllowUsernamePassword, setup.LoginPolicy.AllowRegister, @@ -218,14 +218,14 @@ func (c *Commands) SetUpInstance(ctx context.Context, setup *InstanceSetup) (str setup.LoginPolicy.SecondFactorCheckLifetime, setup.LoginPolicy.MultiFactorCheckLifetime, ), - AddSecondFactorToDefaultLoginPolicy(instanceAgg, domain.SecondFactorTypeOTP), - AddSecondFactorToDefaultLoginPolicy(instanceAgg, domain.SecondFactorTypeU2F), - AddMultiFactorToDefaultLoginPolicy(instanceAgg, domain.MultiFactorTypeU2FWithPIN), + prepareAddSecondFactorToDefaultLoginPolicy(instanceAgg, domain.SecondFactorTypeOTP), + prepareAddSecondFactorToDefaultLoginPolicy(instanceAgg, domain.SecondFactorTypeU2F), + prepareAddMultiFactorToDefaultLoginPolicy(instanceAgg, domain.MultiFactorTypeU2FWithPIN), - AddPrivacyPolicy(instanceAgg, setup.PrivacyPolicy.TOSLink, setup.PrivacyPolicy.PrivacyLink, setup.PrivacyPolicy.HelpLink), - AddDefaultLockoutPolicy(instanceAgg, setup.LockoutPolicy.MaxAttempts, setup.LockoutPolicy.ShouldShowLockoutFailure), + prepareAddDefaultPrivacyPolicy(instanceAgg, setup.PrivacyPolicy.TOSLink, setup.PrivacyPolicy.PrivacyLink, setup.PrivacyPolicy.HelpLink), + prepareAddDefaultLockoutPolicy(instanceAgg, setup.LockoutPolicy.MaxAttempts, setup.LockoutPolicy.ShouldShowLockoutFailure), - AddDefaultLabelPolicy( + prepareAddDefaultLabelPolicy( instanceAgg, setup.LabelPolicy.PrimaryColor, setup.LabelPolicy.BackgroundColor, @@ -239,13 +239,13 @@ func (c *Commands) SetUpInstance(ctx context.Context, setup *InstanceSetup) (str setup.LabelPolicy.ErrorMsgPopup, setup.LabelPolicy.DisableWatermark, ), - ActivateDefaultLabelPolicy(instanceAgg), + prepareActivateDefaultLabelPolicy(instanceAgg), - AddEmailTemplate(instanceAgg, setup.EmailTemplate), + prepareAddDefaultEmailTemplate(instanceAgg, setup.EmailTemplate), } for _, msg := range setup.MessageTexts { - validations = append(validations, SetInstanceCustomTexts(instanceAgg, msg)) + validations = append(validations, prepareSetInstanceCustomMessageTexts(instanceAgg, msg)) } console := &addOIDCApp{ @@ -377,7 +377,7 @@ func (c *Commands) SetDefaultLanguage(ctx context.Context, defaultLanguage langu }, nil } -func addInstance(a *instance.Aggregate, instanceName string, defaultLanguage language.Tag) preparation.Validation { +func prepareAddInstance(a *instance.Aggregate, instanceName string, defaultLanguage language.Tag) preparation.Validation { return func() (preparation.CreateCommands, error) { return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) { return []eventstore.Command{ diff --git a/internal/command/instance_custom_message_text.go b/internal/command/instance_custom_message_text.go index eef6eba58..a70b6c5de 100644 --- a/internal/command/instance_custom_message_text.go +++ b/internal/command/instance_custom_message_text.go @@ -3,11 +3,13 @@ package command import ( "context" + "golang.org/x/text/language" + + "github.com/zitadel/zitadel/internal/command/preparation" "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/repository/instance" - "golang.org/x/text/language" ) func (c *Commands) SetDefaultMessageText(ctx context.Context, instanceID string, messageText *domain.CustomMessageText) (*domain.ObjectDetails, error) { @@ -121,3 +123,83 @@ func (c *Commands) defaultCustomMessageTextWriteModelByID(ctx context.Context, m } return writeModel, nil } + +func prepareSetInstanceCustomMessageTexts( + a *instance.Aggregate, + msg *domain.CustomMessageText, +) preparation.Validation { + return func() (preparation.CreateCommands, error) { + if !msg.IsValid() { + return nil, caos_errs.ThrowInvalidArgument(nil, "INSTANCE-kd9fs", "Errors.CustomMessageText.Invalid") + } + return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) { + existing, err := existingInstanceCustomMessageText(ctx, filter, msg.MessageTextType, msg.Language) + if err != nil { + return nil, err + } + + cmds := make([]eventstore.Command, 0, 7) + if existing.Greeting != msg.Greeting { + if msg.Greeting != "" { + cmds = append(cmds, instance.NewCustomTextSetEvent(ctx, &a.Aggregate, msg.MessageTextType, domain.MessageGreeting, msg.Greeting, msg.Language)) + } else { + cmds = append(cmds, instance.NewCustomTextRemovedEvent(ctx, &a.Aggregate, msg.MessageTextType, domain.MessageGreeting, msg.Language)) + } + } + if existing.Subject != msg.Subject { + if msg.Subject != "" { + cmds = append(cmds, instance.NewCustomTextSetEvent(ctx, &a.Aggregate, msg.MessageTextType, domain.MessageSubject, msg.Subject, msg.Language)) + } else { + cmds = append(cmds, instance.NewCustomTextRemovedEvent(ctx, &a.Aggregate, msg.MessageTextType, domain.MessageSubject, msg.Language)) + } + } + if existing.Title != msg.Title { + if msg.Title != "" { + cmds = append(cmds, instance.NewCustomTextSetEvent(ctx, &a.Aggregate, msg.MessageTextType, domain.MessageTitle, msg.Title, msg.Language)) + } else { + cmds = append(cmds, instance.NewCustomTextRemovedEvent(ctx, &a.Aggregate, msg.MessageTextType, domain.MessageTitle, msg.Language)) + } + } + if existing.PreHeader != msg.PreHeader { + if msg.PreHeader != "" { + cmds = append(cmds, instance.NewCustomTextSetEvent(ctx, &a.Aggregate, msg.MessageTextType, domain.MessagePreHeader, msg.PreHeader, msg.Language)) + } else { + cmds = append(cmds, instance.NewCustomTextRemovedEvent(ctx, &a.Aggregate, msg.MessageTextType, domain.MessagePreHeader, msg.Language)) + } + } + if existing.Text != msg.Text { + if msg.Text != "" { + cmds = append(cmds, instance.NewCustomTextSetEvent(ctx, &a.Aggregate, msg.MessageTextType, domain.MessageText, msg.Text, msg.Language)) + } else { + cmds = append(cmds, instance.NewCustomTextRemovedEvent(ctx, &a.Aggregate, msg.MessageTextType, domain.MessageText, msg.Language)) + } + } + if existing.ButtonText != msg.ButtonText { + if msg.ButtonText != "" { + cmds = append(cmds, instance.NewCustomTextSetEvent(ctx, &a.Aggregate, msg.MessageTextType, domain.MessageButtonText, msg.ButtonText, msg.Language)) + } else { + cmds = append(cmds, instance.NewCustomTextRemovedEvent(ctx, &a.Aggregate, msg.MessageTextType, domain.MessageButtonText, msg.Language)) + } + } + if existing.FooterText != msg.FooterText { + if msg.FooterText != "" { + cmds = append(cmds, instance.NewCustomTextSetEvent(ctx, &a.Aggregate, msg.MessageTextType, domain.MessageFooterText, msg.FooterText, msg.Language)) + } else { + cmds = append(cmds, instance.NewCustomTextRemovedEvent(ctx, &a.Aggregate, msg.MessageTextType, domain.MessageFooterText, msg.Language)) + } + } + return cmds, nil + }, nil + } +} + +func existingInstanceCustomMessageText(ctx context.Context, filter preparation.FilterToQueryReducer, textType string, lang language.Tag) (*InstanceCustomMessageTextWriteModel, error) { + writeModel := NewInstanceCustomMessageTextWriteModel(ctx, textType, lang) + events, err := filter(ctx, writeModel.Query()) + if err != nil { + return nil, err + } + writeModel.AppendEvents(events...) + writeModel.Reduce() + return writeModel, nil +} diff --git a/internal/command/instance_domain_policy.go b/internal/command/instance_domain_policy.go deleted file mode 100644 index 21489fbfa..000000000 --- a/internal/command/instance_domain_policy.go +++ /dev/null @@ -1,29 +0,0 @@ -package command - -import ( - "context" - - "github.com/zitadel/zitadel/internal/command/preparation" - "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/repository/instance" -) - -func AddDefaultDomainPolicy( - a *instance.Aggregate, - userLoginMustBeDomain, - validateOrgDomains, - smtpSenderAddressMatchesInstanceDomain bool, -) preparation.Validation { - return func() (preparation.CreateCommands, error) { - return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) { - //TODO: check if already exists - return []eventstore.Command{ - instance.NewDomainPolicyAddedEvent(ctx, &a.Aggregate, - userLoginMustBeDomain, - validateOrgDomains, - smtpSenderAddressMatchesInstanceDomain, - ), - }, nil - }, nil - } -} diff --git a/internal/command/instance_email_template.go b/internal/command/instance_email_template.go deleted file mode 100644 index 69e7e5d2c..000000000 --- a/internal/command/instance_email_template.go +++ /dev/null @@ -1,106 +0,0 @@ -package command - -import ( - "context" - - "golang.org/x/text/language" - - "github.com/zitadel/zitadel/internal/command/preparation" - "github.com/zitadel/zitadel/internal/domain" - "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/repository/instance" -) - -func AddEmailTemplate( - a *instance.Aggregate, - tempalte []byte, -) preparation.Validation { - return func() (preparation.CreateCommands, error) { - return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) { - //TODO: check if already exists - return []eventstore.Command{ - instance.NewMailTemplateAddedEvent(ctx, &a.Aggregate, - tempalte, - ), - }, nil - }, nil - } -} - -func SetInstanceCustomTexts( - a *instance.Aggregate, - msg *domain.CustomMessageText, -) preparation.Validation { - return func() (preparation.CreateCommands, error) { - return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) { - existing, err := existingInstanceCustomMessageText(ctx, filter, msg.MessageTextType, msg.Language) - if err != nil { - return nil, err - } - - cmds := make([]eventstore.Command, 0, 7) - if existing.Greeting != msg.Greeting { - if msg.Greeting != "" { - cmds = append(cmds, instance.NewCustomTextSetEvent(ctx, &a.Aggregate, msg.MessageTextType, domain.MessageGreeting, msg.Greeting, msg.Language)) - } else { - cmds = append(cmds, instance.NewCustomTextRemovedEvent(ctx, &a.Aggregate, msg.MessageTextType, domain.MessageGreeting, msg.Language)) - } - } - if existing.Subject != msg.Subject { - if msg.Subject != "" { - cmds = append(cmds, instance.NewCustomTextSetEvent(ctx, &a.Aggregate, msg.MessageTextType, domain.MessageSubject, msg.Subject, msg.Language)) - } else { - cmds = append(cmds, instance.NewCustomTextRemovedEvent(ctx, &a.Aggregate, msg.MessageTextType, domain.MessageSubject, msg.Language)) - } - } - if existing.Title != msg.Title { - if msg.Title != "" { - cmds = append(cmds, instance.NewCustomTextSetEvent(ctx, &a.Aggregate, msg.MessageTextType, domain.MessageTitle, msg.Title, msg.Language)) - } else { - cmds = append(cmds, instance.NewCustomTextRemovedEvent(ctx, &a.Aggregate, msg.MessageTextType, domain.MessageTitle, msg.Language)) - } - } - if existing.PreHeader != msg.PreHeader { - if msg.PreHeader != "" { - cmds = append(cmds, instance.NewCustomTextSetEvent(ctx, &a.Aggregate, msg.MessageTextType, domain.MessagePreHeader, msg.PreHeader, msg.Language)) - } else { - cmds = append(cmds, instance.NewCustomTextRemovedEvent(ctx, &a.Aggregate, msg.MessageTextType, domain.MessagePreHeader, msg.Language)) - } - } - if existing.Text != msg.Text { - if msg.Text != "" { - cmds = append(cmds, instance.NewCustomTextSetEvent(ctx, &a.Aggregate, msg.MessageTextType, domain.MessageText, msg.Text, msg.Language)) - } else { - cmds = append(cmds, instance.NewCustomTextRemovedEvent(ctx, &a.Aggregate, msg.MessageTextType, domain.MessageText, msg.Language)) - } - } - if existing.ButtonText != msg.ButtonText { - if msg.ButtonText != "" { - cmds = append(cmds, instance.NewCustomTextSetEvent(ctx, &a.Aggregate, msg.MessageTextType, domain.MessageButtonText, msg.ButtonText, msg.Language)) - } else { - cmds = append(cmds, instance.NewCustomTextRemovedEvent(ctx, &a.Aggregate, msg.MessageTextType, domain.MessageButtonText, msg.Language)) - } - } - if existing.FooterText != msg.FooterText { - if msg.FooterText != "" { - cmds = append(cmds, instance.NewCustomTextSetEvent(ctx, &a.Aggregate, msg.MessageTextType, domain.MessageFooterText, msg.FooterText, msg.Language)) - } else { - cmds = append(cmds, instance.NewCustomTextRemovedEvent(ctx, &a.Aggregate, msg.MessageTextType, domain.MessageFooterText, msg.Language)) - } - } - // TODO: what if no text changed? len(events) == 0 - return cmds, nil - }, nil - } -} - -func existingInstanceCustomMessageText(ctx context.Context, filter preparation.FilterToQueryReducer, textType string, lang language.Tag) (*InstanceCustomMessageTextWriteModel, error) { - writeModel := NewInstanceCustomMessageTextWriteModel(ctx, textType, lang) - events, err := filter(ctx, writeModel.Query()) - if err != nil { - return nil, err - } - writeModel.AppendEvents(events...) - writeModel.Reduce() - return writeModel, nil -} diff --git a/internal/command/instance_label_policy.go b/internal/command/instance_label_policy.go deleted file mode 100644 index 891e29911..000000000 --- a/internal/command/instance_label_policy.go +++ /dev/null @@ -1,58 +0,0 @@ -package command - -import ( - "context" - - "github.com/zitadel/zitadel/internal/command/preparation" - "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/repository/instance" -) - -func AddDefaultLabelPolicy( - a *instance.Aggregate, - primaryColor, - backgroundColor, - warnColor, - fontColor, - primaryColorDark, - backgroundColorDark, - warnColorDark, - fontColorDark string, - hideLoginNameSuffix, - errorMsgPopup, - disableWatermark bool, -) preparation.Validation { - return func() (preparation.CreateCommands, error) { - return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) { - //TODO: check if already exists - return []eventstore.Command{ - instance.NewLabelPolicyAddedEvent(ctx, &a.Aggregate, - primaryColor, - backgroundColor, - warnColor, - fontColor, - primaryColorDark, - backgroundColorDark, - warnColorDark, - fontColorDark, - hideLoginNameSuffix, - errorMsgPopup, - disableWatermark, - ), - }, nil - }, nil - } -} - -func ActivateDefaultLabelPolicy( - a *instance.Aggregate, -) preparation.Validation { - return func() (preparation.CreateCommands, error) { - return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) { - //TODO: check if already exists - return []eventstore.Command{ - instance.NewLabelPolicyActivatedEvent(ctx, &a.Aggregate), - }, nil - }, nil - } -} diff --git a/internal/command/instance_lockout_policy.go b/internal/command/instance_lockout_policy.go deleted file mode 100644 index 84245a1fe..000000000 --- a/internal/command/instance_lockout_policy.go +++ /dev/null @@ -1,24 +0,0 @@ -package command - -import ( - "context" - - "github.com/zitadel/zitadel/internal/command/preparation" - "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/repository/instance" -) - -func AddDefaultLockoutPolicy( - a *instance.Aggregate, - maxAttempts uint64, - showLockoutFailure bool, -) preparation.Validation { - return func() (preparation.CreateCommands, error) { - return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) { - //TODO: check if already exists - return []eventstore.Command{ - instance.NewLockoutPolicyAddedEvent(ctx, &a.Aggregate, maxAttempts, showLockoutFailure), - }, nil - }, nil - } -} diff --git a/internal/command/instance_login_policy.go b/internal/command/instance_login_policy.go deleted file mode 100644 index f76571ea2..000000000 --- a/internal/command/instance_login_policy.go +++ /dev/null @@ -1,73 +0,0 @@ -package command - -import ( - "context" - "time" - - "github.com/zitadel/zitadel/internal/command/preparation" - "github.com/zitadel/zitadel/internal/domain" - "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/repository/instance" -) - -func AddDefaultLoginPolicy( - a *instance.Aggregate, - allowUsernamePassword bool, - allowRegister bool, - allowExternalIDP bool, - forceMFA bool, - hidePasswordReset bool, - ignoreUnknownUsernames bool, - passwordlessType domain.PasswordlessType, - defaultRedirectURI string, - passwordCheckLifetime time.Duration, - externalLoginCheckLifetime time.Duration, - mfaInitSkipLifetime time.Duration, - secondFactorCheckLifetime time.Duration, - multiFactorCheckLifetime time.Duration, -) preparation.Validation { - return func() (preparation.CreateCommands, error) { - return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) { - //TODO: check if already exists - return []eventstore.Command{ - instance.NewLoginPolicyAddedEvent(ctx, &a.Aggregate, - allowUsernamePassword, - allowRegister, - allowExternalIDP, - forceMFA, - hidePasswordReset, - ignoreUnknownUsernames, - passwordlessType, - defaultRedirectURI, - passwordCheckLifetime, - externalLoginCheckLifetime, - mfaInitSkipLifetime, - secondFactorCheckLifetime, - multiFactorCheckLifetime, - ), - }, nil - }, nil - } -} - -func AddSecondFactorToDefaultLoginPolicy(a *instance.Aggregate, factor domain.SecondFactorType) preparation.Validation { - return func() (preparation.CreateCommands, error) { - return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) { - //TODO: check if already exists - return []eventstore.Command{ - instance.NewLoginPolicySecondFactorAddedEvent(ctx, &a.Aggregate, factor), - }, nil - }, nil - } -} - -func AddMultiFactorToDefaultLoginPolicy(a *instance.Aggregate, factor domain.MultiFactorType) preparation.Validation { - return func() (preparation.CreateCommands, error) { - return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) { - //TODO: check if already exists - return []eventstore.Command{ - instance.NewLoginPolicyMultiFactorAddedEvent(ctx, &a.Aggregate, factor), - }, nil - }, nil - } -} diff --git a/internal/command/instance_password_age_policy.go b/internal/command/instance_password_age_policy.go deleted file mode 100644 index e00dbfbfe..000000000 --- a/internal/command/instance_password_age_policy.go +++ /dev/null @@ -1,27 +0,0 @@ -package command - -import ( - "context" - - "github.com/zitadel/zitadel/internal/command/preparation" - "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/repository/instance" -) - -func AddPasswordAgePolicy( - a *instance.Aggregate, - expireWarnDays, - maxAgeDays uint64, -) preparation.Validation { - return func() (preparation.CreateCommands, error) { - return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) { - //TODO: check if already exists - return []eventstore.Command{ - instance.NewPasswordAgePolicyAddedEvent(ctx, &a.Aggregate, - expireWarnDays, - maxAgeDays, - ), - }, nil - }, nil - } -} diff --git a/internal/command/instance_password_complexity_policy.go b/internal/command/instance_password_complexity_policy.go deleted file mode 100644 index c2f042c17..000000000 --- a/internal/command/instance_password_complexity_policy.go +++ /dev/null @@ -1,33 +0,0 @@ -package command - -import ( - "context" - - "github.com/zitadel/zitadel/internal/command/preparation" - "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/repository/instance" -) - -func AddPasswordComplexityPolicy( - a *instance.Aggregate, - minLength uint64, - hasLowercase, - hasUppercase, - hasNumber, - hasSymbol bool, -) preparation.Validation { - return func() (preparation.CreateCommands, error) { - return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) { - //TODO: check if already exists - return []eventstore.Command{ - instance.NewPasswordComplexityPolicyAddedEvent(ctx, &a.Aggregate, - minLength, - hasLowercase, - hasUppercase, - hasNumber, - hasSymbol, - ), - }, nil - }, nil - } -} diff --git a/internal/command/instance_policy_domain.go b/internal/command/instance_policy_domain.go index 8cb761669..a49907e35 100644 --- a/internal/command/instance_policy_domain.go +++ b/internal/command/instance_policy_domain.go @@ -3,41 +3,26 @@ package command import ( "context" + "github.com/zitadel/zitadel/internal/api/authz" + "github.com/zitadel/zitadel/internal/command/preparation" "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" - iam_repo "github.com/zitadel/zitadel/internal/repository/instance" + "github.com/zitadel/zitadel/internal/repository/instance" "github.com/zitadel/zitadel/internal/telemetry/tracing" ) -func (c *Commands) AddDefaultDomainPolicy(ctx context.Context, policy *domain.DomainPolicy) (*domain.DomainPolicy, error) { - addedPolicy := NewInstanceDomainPolicyWriteModel(ctx) - instanceAgg := InstanceAggregateFromWriteModel(&addedPolicy.WriteModel) - event, err := c.addDefaultDomainPolicy(ctx, instanceAgg, addedPolicy, policy) +func (c *Commands) AddDefaultDomainPolicy(ctx context.Context, userLoginMustBeDomain, validateOrgDomains, smtpSenderAddressMatchesInstanceDomain bool) (*domain.ObjectDetails, error) { + instanceAgg := instance.NewAggregate(authz.GetInstance(ctx).InstanceID()) + cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, prepareAddDefaultDomainPolicy(instanceAgg, userLoginMustBeDomain, validateOrgDomains, smtpSenderAddressMatchesInstanceDomain)) if err != nil { return nil, err } - - pushedEvents, err := c.eventstore.Push(ctx, event) + pushedEvents, err := c.eventstore.Push(ctx, cmds...) if err != nil { return nil, err } - err = AppendAndReduce(addedPolicy, pushedEvents...) - if err != nil { - return nil, err - } - return writeModelToDomainPolicy(addedPolicy), nil -} - -func (c *Commands) addDefaultDomainPolicy(ctx context.Context, instanceAgg *eventstore.Aggregate, addedPolicy *InstanceDomainPolicyWriteModel, policy *domain.DomainPolicy) (eventstore.Command, error) { - err := c.eventstore.FilterToQueryReducer(ctx, addedPolicy) - if err != nil { - return nil, err - } - if addedPolicy.State == domain.PolicyStateActive { - return nil, caos_errs.ThrowAlreadyExists(nil, "INSTANCE-Lk0dS", "Errors.IAM.DomainPolicy.AlreadyExists") - } - return iam_repo.NewDomainPolicyAddedEvent(ctx, instanceAgg, policy.UserLoginMustBeDomain, policy.ValidateOrgDomains, policy.SMTPSenderAddressMatchesInstanceDomain), nil + return pushedEventsToObjectDetails(pushedEvents), nil } func (c *Commands) ChangeDefaultDomainPolicy(ctx context.Context, policy *domain.DomainPolicy) (*domain.DomainPolicy, error) { @@ -90,3 +75,34 @@ func (c *Commands) defaultDomainPolicyWriteModelByID(ctx context.Context) (polic } return writeModel, nil } + +func prepareAddDefaultDomainPolicy( + a *instance.Aggregate, + userLoginMustBeDomain, + validateOrgDomains, + smtpSenderAddressMatchesInstanceDomain bool, +) preparation.Validation { + return func() (preparation.CreateCommands, error) { + return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) { + writeModel := NewInstanceDomainPolicyWriteModel(ctx) + events, err := filter(ctx, writeModel.Query()) + if err != nil { + return nil, err + } + writeModel.AppendEvents(events...) + if err = writeModel.Reduce(); err != nil { + return nil, err + } + if writeModel.State == domain.PolicyStateActive { + return nil, caos_errs.ThrowAlreadyExists(nil, "INSTANCE-Lk0dS", "Errors.Instance.DomainPolicy.AlreadyExists") + } + return []eventstore.Command{ + instance.NewDomainPolicyAddedEvent(ctx, &a.Aggregate, + userLoginMustBeDomain, + validateOrgDomains, + smtpSenderAddressMatchesInstanceDomain, + ), + }, nil + }, nil + } +} diff --git a/internal/command/instance_policy_domain_test.go b/internal/command/instance_policy_domain_test.go index 64e0db0db..1a44b0716 100644 --- a/internal/command/instance_policy_domain_test.go +++ b/internal/command/instance_policy_domain_test.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" @@ -20,11 +21,13 @@ func TestCommandSide_AddDefaultDomainPolicy(t *testing.T) { eventstore *eventstore.Eventstore } type args struct { - ctx context.Context - policy *domain.DomainPolicy + ctx context.Context + userLoginMustBeDomain bool + validateOrgDomains bool + smtpSenderAddressMatchesInstanceDomain bool } type res struct { - want *domain.DomainPolicy + want *domain.ObjectDetails err func(error) bool } tests := []struct { @@ -51,12 +54,10 @@ func TestCommandSide_AddDefaultDomainPolicy(t *testing.T) { ), }, args: args{ - ctx: context.Background(), - policy: &domain.DomainPolicy{ - UserLoginMustBeDomain: true, - ValidateOrgDomains: true, - SMTPSenderAddressMatchesInstanceDomain: true, - }, + ctx: context.Background(), + userLoginMustBeDomain: true, + validateOrgDomains: true, + smtpSenderAddressMatchesInstanceDomain: true, }, res: res{ err: caos_errs.IsErrorAlreadyExists, @@ -84,23 +85,14 @@ func TestCommandSide_AddDefaultDomainPolicy(t *testing.T) { ), }, args: args{ - ctx: authz.WithInstanceID(context.Background(), "INSTANCE"), - policy: &domain.DomainPolicy{ - UserLoginMustBeDomain: true, - ValidateOrgDomains: true, - SMTPSenderAddressMatchesInstanceDomain: true, - }, + ctx: authz.WithInstanceID(context.Background(), "INSTANCE"), + userLoginMustBeDomain: true, + validateOrgDomains: true, + smtpSenderAddressMatchesInstanceDomain: true, }, res: res{ - want: &domain.DomainPolicy{ - ObjectRoot: models.ObjectRoot{ - InstanceID: "INSTANCE", - AggregateID: "INSTANCE", - ResourceOwner: "INSTANCE", - }, - UserLoginMustBeDomain: true, - ValidateOrgDomains: true, - SMTPSenderAddressMatchesInstanceDomain: true, + want: &domain.ObjectDetails{ + ResourceOwner: "INSTANCE", }, }, }, @@ -110,7 +102,7 @@ func TestCommandSide_AddDefaultDomainPolicy(t *testing.T) { r := &Commands{ eventstore: tt.fields.eventstore, } - got, err := r.AddDefaultDomainPolicy(tt.args.ctx, tt.args.policy) + got, err := r.AddDefaultDomainPolicy(tt.args.ctx, tt.args.userLoginMustBeDomain, tt.args.validateOrgDomains, tt.args.smtpSenderAddressMatchesInstanceDomain) if tt.res.err == nil { assert.NoError(t, err) } diff --git a/internal/command/instance_policy_label.go b/internal/command/instance_policy_label.go index 2b0c17cba..19bd63004 100644 --- a/internal/command/instance_policy_label.go +++ b/internal/command/instance_policy_label.go @@ -4,6 +4,7 @@ import ( "context" "github.com/zitadel/zitadel/internal/api/authz" + "github.com/zitadel/zitadel/internal/command/preparation" "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" @@ -11,52 +12,35 @@ import ( "github.com/zitadel/zitadel/internal/telemetry/tracing" ) -func (c *Commands) AddDefaultLabelPolicy(ctx context.Context, policy *domain.LabelPolicy) (*domain.LabelPolicy, error) { - addedPolicy := NewInstanceLabelPolicyWriteModel(ctx) - instanceAgg := InstanceAggregateFromWriteModel(&addedPolicy.LabelPolicyWriteModel.WriteModel) - event, err := c.addDefaultLabelPolicy(ctx, instanceAgg, addedPolicy, policy) +func (c *Commands) AddDefaultLabelPolicy( + ctx context.Context, + primaryColor, backgroundColor, warnColor, fontColor, primaryColorDark, backgroundColorDark, warnColorDark, fontColorDark string, + hideLoginNameSuffix, errorMsgPopup, disableWatermark bool, +) (*domain.ObjectDetails, error) { + instanceAgg := instance.NewAggregate(authz.GetInstance(ctx).InstanceID()) + cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, + prepareAddDefaultLabelPolicy( + instanceAgg, + primaryColor, + backgroundColor, + warnColor, + fontColor, + primaryColorDark, + backgroundColorDark, + warnColorDark, + fontColorDark, + hideLoginNameSuffix, + errorMsgPopup, + disableWatermark, + )) if err != nil { return nil, err } - - pushedEvents, err := c.eventstore.Push(ctx, event) + pushedEvents, err := c.eventstore.Push(ctx, cmds...) if err != nil { return nil, err } - err = AppendAndReduce(addedPolicy, pushedEvents...) - if err != nil { - return nil, err - } - return writeModelToLabelPolicy(&addedPolicy.LabelPolicyWriteModel), nil -} - -func (c *Commands) addDefaultLabelPolicy(ctx context.Context, instanceAgg *eventstore.Aggregate, addedPolicy *InstanceLabelPolicyWriteModel, policy *domain.LabelPolicy) (eventstore.Command, error) { - if err := policy.IsValid(); err != nil { - return nil, err - } - err := c.eventstore.FilterToQueryReducer(ctx, addedPolicy) - if err != nil { - return nil, err - } - if addedPolicy.State == domain.PolicyStateActive { - return nil, caos_errs.ThrowAlreadyExists(nil, "INSTANCE-2B0ps", "Errors.IAM.LabelPolicy.AlreadyExists") - } - - return instance.NewLabelPolicyAddedEvent( - ctx, - instanceAgg, - policy.PrimaryColor, - policy.BackgroundColor, - policy.WarnColor, - policy.FontColor, - policy.PrimaryColorDark, - policy.BackgroundColorDark, - policy.WarnColorDark, - policy.FontColorDark, - policy.HideLoginNameSuffix, - policy.ErrorMsgPopup, - policy.DisableWatermark), nil - + return pushedEventsToObjectDetails(pushedEvents), nil } func (c *Commands) ChangeDefaultLabelPolicy(ctx context.Context, policy *domain.LabelPolicy) (*domain.LabelPolicy, error) { @@ -102,26 +86,16 @@ func (c *Commands) ChangeDefaultLabelPolicy(ctx context.Context, policy *domain. } func (c *Commands) ActivateDefaultLabelPolicy(ctx context.Context) (*domain.ObjectDetails, error) { - existingPolicy, err := c.defaultLabelPolicyWriteModelByID(ctx) + instanceAgg := instance.NewAggregate(authz.GetInstance(ctx).InstanceID()) + cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, prepareActivateDefaultLabelPolicy(instanceAgg)) if err != nil { return nil, err } - - if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved { - return nil, caos_errs.ThrowNotFound(nil, "INSTANCE-6M23e", "Errors.IAM.LabelPolicy.NotFound") - } - - instanceAgg := InstanceAggregateFromWriteModel(&existingPolicy.LabelPolicyWriteModel.WriteModel) - pushedEvents, err := c.eventstore.Push(ctx, instance.NewLabelPolicyActivatedEvent(ctx, instanceAgg)) + pushedEvents, err := c.eventstore.Push(ctx, cmds...) if err != nil { return nil, err } - err = AppendAndReduce(existingPolicy, pushedEvents...) - if err != nil { - return nil, err - } - - return writeModelToObjectDetails(&existingPolicy.LabelPolicyWriteModel.WriteModel), nil + return pushedEventsToObjectDetails(pushedEvents), nil } func (c *Commands) AddLogoDefaultLabelPolicy(ctx context.Context, upload *AssetUpload) (*domain.ObjectDetails, error) { @@ -396,3 +370,74 @@ func (c *Commands) getDefaultLabelPolicy(ctx context.Context) (*domain.LabelPoli policy.Default = true return policy, nil } + +func prepareAddDefaultLabelPolicy( + a *instance.Aggregate, + primaryColor, + backgroundColor, + warnColor, + fontColor, + primaryColorDark, + backgroundColorDark, + warnColorDark, + fontColorDark string, + hideLoginNameSuffix, + errorMsgPopup, + disableWatermark bool, +) preparation.Validation { + return func() (preparation.CreateCommands, error) { + return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) { + writeModel := NewInstanceLabelPolicyWriteModel(ctx) + events, err := filter(ctx, writeModel.Query()) + if err != nil { + return nil, err + } + writeModel.AppendEvents(events...) + if err = writeModel.Reduce(); err != nil { + return nil, err + } + if writeModel.State == domain.PolicyStateActive { + return nil, caos_errs.ThrowAlreadyExists(nil, "INSTANCE-2B0ps", "Errors.Instance.LabelPolicy.AlreadyExists") + } + return []eventstore.Command{ + instance.NewLabelPolicyAddedEvent(ctx, &a.Aggregate, + primaryColor, + backgroundColor, + warnColor, + fontColor, + primaryColorDark, + backgroundColorDark, + warnColorDark, + fontColorDark, + hideLoginNameSuffix, + errorMsgPopup, + disableWatermark, + ), + }, nil + }, nil + } +} + +func prepareActivateDefaultLabelPolicy( + a *instance.Aggregate, +) preparation.Validation { + return func() (preparation.CreateCommands, error) { + return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) { + writeModel := NewInstanceLabelPolicyWriteModel(ctx) + events, err := filter(ctx, writeModel.Query()) + if err != nil { + return nil, err + } + writeModel.AppendEvents(events...) + if err = writeModel.Reduce(); err != nil { + return nil, err + } + if !writeModel.State.Exists() { + return nil, caos_errs.ThrowNotFound(nil, "INSTANCE-6M23e", "Errors.Instance.LabelPolicy.NotFound") + } + return []eventstore.Command{ + instance.NewLabelPolicyActivatedEvent(ctx, &a.Aggregate), + }, nil + }, nil + } +} diff --git a/internal/command/instance_policy_label_test.go b/internal/command/instance_policy_label_test.go index b0d45221b..aa2576a49 100644 --- a/internal/command/instance_policy_label_test.go +++ b/internal/command/instance_policy_label_test.go @@ -25,11 +25,21 @@ func TestCommandSide_AddDefaultLabelPolicy(t *testing.T) { eventstore *eventstore.Eventstore } type args struct { - ctx context.Context - policy *domain.LabelPolicy + ctx context.Context + primaryColor string + backgroundColor string + warnColor string + fontColor string + primaryColorDark string + backgroundColorDark string + warnColorDark string + fontColorDark string + hideLoginNameSuffix bool + errorMsgPopup bool + disableWatermark bool } type res struct { - want *domain.LabelPolicy + want *domain.ObjectDetails err func(error) bool } tests := []struct { @@ -64,20 +74,18 @@ func TestCommandSide_AddDefaultLabelPolicy(t *testing.T) { ), }, args: args{ - ctx: context.Background(), - policy: &domain.LabelPolicy{ - PrimaryColor: "#ffffff", - BackgroundColor: "#ffffff", - WarnColor: "#ffffff", - FontColor: "#ffffff", - PrimaryColorDark: "#ffffff", - BackgroundColorDark: "#ffffff", - WarnColorDark: "#ffffff", - FontColorDark: "#ffffff", - HideLoginNameSuffix: true, - ErrorMsgPopup: true, - DisableWatermark: true, - }, + ctx: context.Background(), + primaryColor: "#ffffff", + backgroundColor: "#ffffff", + warnColor: "#ffffff", + fontColor: "#ffffff", + primaryColorDark: "#ffffff", + backgroundColorDark: "#ffffff", + warnColorDark: "#ffffff", + fontColorDark: "#ffffff", + hideLoginNameSuffix: true, + errorMsgPopup: true, + disableWatermark: true, }, res: res{ err: caos_errs.IsErrorAlreadyExists, @@ -113,39 +121,22 @@ func TestCommandSide_AddDefaultLabelPolicy(t *testing.T) { ), }, args: args{ - ctx: authz.WithInstanceID(context.Background(), "INSTANCE"), - policy: &domain.LabelPolicy{ - PrimaryColor: "#ffffff", - BackgroundColor: "#ffffff", - WarnColor: "#ffffff", - FontColor: "#ffffff", - PrimaryColorDark: "#ffffff", - BackgroundColorDark: "#ffffff", - WarnColorDark: "#ffffff", - FontColorDark: "#ffffff", - HideLoginNameSuffix: true, - ErrorMsgPopup: true, - DisableWatermark: true, - }, + ctx: authz.WithInstanceID(context.Background(), "INSTANCE"), + primaryColor: "#ffffff", + backgroundColor: "#ffffff", + warnColor: "#ffffff", + fontColor: "#ffffff", + primaryColorDark: "#ffffff", + backgroundColorDark: "#ffffff", + warnColorDark: "#ffffff", + fontColorDark: "#ffffff", + hideLoginNameSuffix: true, + errorMsgPopup: true, + disableWatermark: true, }, res: res{ - want: &domain.LabelPolicy{ - ObjectRoot: models.ObjectRoot{ - InstanceID: "INSTANCE", - AggregateID: "INSTANCE", - ResourceOwner: "INSTANCE", - }, - PrimaryColor: "#ffffff", - BackgroundColor: "#ffffff", - WarnColor: "#ffffff", - FontColor: "#ffffff", - PrimaryColorDark: "#ffffff", - BackgroundColorDark: "#ffffff", - WarnColorDark: "#ffffff", - FontColorDark: "#ffffff", - HideLoginNameSuffix: true, - ErrorMsgPopup: true, - DisableWatermark: true, + want: &domain.ObjectDetails{ + ResourceOwner: "INSTANCE", }, }, }, @@ -155,7 +146,20 @@ func TestCommandSide_AddDefaultLabelPolicy(t *testing.T) { r := &Commands{ eventstore: tt.fields.eventstore, } - got, err := r.AddDefaultLabelPolicy(tt.args.ctx, tt.args.policy) + got, err := r.AddDefaultLabelPolicy( + tt.args.ctx, + tt.args.primaryColor, + tt.args.backgroundColor, + tt.args.warnColor, + tt.args.fontColor, + tt.args.primaryColorDark, + tt.args.backgroundColorDark, + tt.args.warnColorDark, + tt.args.fontColorDark, + tt.args.hideLoginNameSuffix, + tt.args.errorMsgPopup, + tt.args.disableWatermark, + ) if tt.res.err == nil { assert.NoError(t, err) } @@ -378,7 +382,7 @@ func TestCommandSide_ActivateDefaultLabelPolicy(t *testing.T) { ), }, args: args{ - ctx: context.Background(), + ctx: authz.WithInstanceID(context.Background(), "INSTANCE"), }, res: res{ err: caos_errs.IsNotFound, @@ -409,7 +413,8 @@ func TestCommandSide_ActivateDefaultLabelPolicy(t *testing.T) { ), expectPush( []*repository.Event{ - eventFromEventPusher( + eventFromEventPusherWithInstanceID( + "INSTANCE", instance.NewLabelPolicyActivatedEvent(context.Background(), &instance.NewAggregate("INSTANCE").Aggregate, ), @@ -419,7 +424,7 @@ func TestCommandSide_ActivateDefaultLabelPolicy(t *testing.T) { ), }, args: args{ - ctx: context.Background(), + ctx: authz.WithInstanceID(context.Background(), "INSTANCE"), }, res: res{ want: &domain.ObjectDetails{ diff --git a/internal/command/instance_policy_login.go b/internal/command/instance_policy_login.go index ccca5caba..9a6dac96e 100644 --- a/internal/command/instance_policy_login.go +++ b/internal/command/instance_policy_login.go @@ -2,9 +2,12 @@ package command import ( "context" + "time" "github.com/zitadel/logging" + "github.com/zitadel/zitadel/internal/api/authz" + "github.com/zitadel/zitadel/internal/command/preparation" "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" @@ -12,59 +15,35 @@ import ( "github.com/zitadel/zitadel/internal/telemetry/tracing" ) -func (c *Commands) getDefaultLoginPolicy(ctx context.Context) (*domain.LoginPolicy, error) { - policyWriteModel := NewInstanceLoginPolicyWriteModel(ctx) - err := c.eventstore.FilterToQueryReducer(ctx, policyWriteModel) +func (c *Commands) AddDefaultLoginPolicy( + ctx context.Context, + allowUsernamePassword, allowRegister, allowExternalIDP, forceMFA, hidePasswordReset, ignoreUnknownUsernames bool, + passwordlessType domain.PasswordlessType, + defaultRedirectURI string, + passwordCheckLifetime, externalLoginCheckLifetime, mfaInitSkipLifetime, secondFactorCheckLifetime, multiFactorCheckLifetime time.Duration, +) (*domain.ObjectDetails, error) { + instanceAgg := instance.NewAggregate(authz.GetInstance(ctx).InstanceID()) + cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, prepareAddDefaultLoginPolicy(instanceAgg, allowUsernamePassword, + allowRegister, + allowExternalIDP, + forceMFA, + hidePasswordReset, + ignoreUnknownUsernames, + passwordlessType, + defaultRedirectURI, + passwordCheckLifetime, + externalLoginCheckLifetime, + mfaInitSkipLifetime, + secondFactorCheckLifetime, + multiFactorCheckLifetime)) if err != nil { return nil, err } - policy := writeModelToLoginPolicy(&policyWriteModel.LoginPolicyWriteModel) - policy.Default = true - return policy, nil -} - -func (c *Commands) AddDefaultLoginPolicy(ctx context.Context, policy *domain.LoginPolicy) (*domain.LoginPolicy, error) { - addedPolicy := NewInstanceLoginPolicyWriteModel(ctx) - instanceAgg := InstanceAggregateFromWriteModel(&addedPolicy.WriteModel) - event, err := c.addDefaultLoginPolicy(ctx, instanceAgg, addedPolicy, policy) + pushedEvents, err := c.eventstore.Push(ctx, cmds...) if err != nil { return nil, err } - pushedEvents, err := c.eventstore.Push(ctx, event) - if err != nil { - return nil, err - } - err = AppendAndReduce(addedPolicy, pushedEvents...) - if err != nil { - return nil, err - } - return writeModelToLoginPolicy(&addedPolicy.LoginPolicyWriteModel), nil -} - -func (c *Commands) addDefaultLoginPolicy(ctx context.Context, instanceAgg *eventstore.Aggregate, addedPolicy *InstanceLoginPolicyWriteModel, policy *domain.LoginPolicy) (eventstore.Command, error) { - err := c.eventstore.FilterToQueryReducer(ctx, addedPolicy) - if err != nil { - return nil, err - } - if addedPolicy.State == domain.PolicyStateActive { - return nil, caos_errs.ThrowAlreadyExists(nil, "INSTANCE-2B0ps", "Errors.IAM.LoginPolicy.AlreadyExists") - } - - return instance.NewLoginPolicyAddedEvent(ctx, - instanceAgg, - policy.AllowUsernamePassword, - policy.AllowRegister, - policy.AllowExternalIDP, - policy.ForceMFA, - policy.HidePasswordReset, - policy.IgnoreUnknownUsernames, - policy.PasswordlessType, - policy.DefaultRedirectURI, - policy.PasswordCheckLifetime, - policy.ExternalLoginCheckLifetime, - policy.MFAInitSkipLifetime, - policy.SecondFactorCheckLifetime, - policy.MultiFactorCheckLifetime), nil + return pushedEventsToObjectDetails(pushedEvents), nil } func (c *Commands) ChangeDefaultLoginPolicy(ctx context.Context, policy *domain.LoginPolicy) (*domain.LoginPolicy, error) { @@ -210,38 +189,17 @@ func (c *Commands) removeIDPProviderFromDefaultLoginPolicy(ctx context.Context, return events } -func (c *Commands) AddSecondFactorToDefaultLoginPolicy(ctx context.Context, secondFactor domain.SecondFactorType) (domain.SecondFactorType, *domain.ObjectDetails, error) { - if !secondFactor.Valid() { - return domain.SecondFactorTypeUnspecified, nil, caos_errs.ThrowInvalidArgument(nil, "INSTANCE-5m9fs", "Errors.IAM.LoginPolicy.MFA.Unspecified") - } - secondFactorModel := NewInstanceSecondFactorWriteModel(ctx, secondFactor) - instanceAgg := InstanceAggregateFromWriteModel(&secondFactorModel.SecondFactorWriteModel.WriteModel) - event, err := c.addSecondFactorToDefaultLoginPolicy(ctx, instanceAgg, secondFactorModel, secondFactor) - if err != nil { - return domain.SecondFactorTypeUnspecified, nil, err - } - - pushedEvents, err := c.eventstore.Push(ctx, event) - if err != nil { - return domain.SecondFactorTypeUnspecified, nil, err - } - err = AppendAndReduce(secondFactorModel, pushedEvents...) - if err != nil { - return domain.SecondFactorTypeUnspecified, nil, err - } - return secondFactorModel.MFAType, writeModelToObjectDetails(&secondFactorModel.WriteModel), nil -} - -func (c *Commands) addSecondFactorToDefaultLoginPolicy(ctx context.Context, instanceAgg *eventstore.Aggregate, secondFactorModel *InstanceSecondFactorWriteModel, secondFactor domain.SecondFactorType) (eventstore.Command, error) { - err := c.eventstore.FilterToQueryReducer(ctx, secondFactorModel) +func (c *Commands) AddSecondFactorToDefaultLoginPolicy(ctx context.Context, secondFactor domain.SecondFactorType) (*domain.ObjectDetails, error) { + instanceAgg := instance.NewAggregate(authz.GetInstance(ctx).InstanceID()) + cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, prepareAddSecondFactorToDefaultLoginPolicy(instanceAgg, secondFactor)) if err != nil { return nil, err } - - if secondFactorModel.State == domain.FactorStateActive { - return nil, caos_errs.ThrowAlreadyExists(nil, "INSTANCE-2B0ps", "Errors.IAM.LoginPolicy.MFA.AlreadyExists") + pushedEvents, err := c.eventstore.Push(ctx, cmds...) + if err != nil { + return nil, err } - return instance.NewLoginPolicySecondFactorAddedEvent(ctx, instanceAgg, secondFactor), nil + return pushedEventsToObjectDetails(pushedEvents), nil } func (c *Commands) RemoveSecondFactorFromDefaultLoginPolicy(ctx context.Context, secondFactor domain.SecondFactorType) (*domain.ObjectDetails, error) { @@ -268,38 +226,17 @@ func (c *Commands) RemoveSecondFactorFromDefaultLoginPolicy(ctx context.Context, return writeModelToObjectDetails(&secondFactorModel.WriteModel), nil } -func (c *Commands) AddMultiFactorToDefaultLoginPolicy(ctx context.Context, multiFactor domain.MultiFactorType) (domain.MultiFactorType, *domain.ObjectDetails, error) { - if !multiFactor.Valid() { - return domain.MultiFactorTypeUnspecified, nil, caos_errs.ThrowInvalidArgument(nil, "INSTANCE-5m9fs", "Errors.IAM.LoginPolicy.MFA.Unspecified") - } - multiFactorModel := NewInstanceMultiFactorWriteModel(ctx, multiFactor) - instanceAgg := InstanceAggregateFromWriteModel(&multiFactorModel.MultiFactorWriteModel.WriteModel) - event, err := c.addMultiFactorToDefaultLoginPolicy(ctx, instanceAgg, multiFactorModel, multiFactor) - if err != nil { - return domain.MultiFactorTypeUnspecified, nil, err - } - - pushedEvents, err := c.eventstore.Push(ctx, event) - if err != nil { - return domain.MultiFactorTypeUnspecified, nil, err - } - err = AppendAndReduce(multiFactorModel, pushedEvents...) - if err != nil { - return domain.MultiFactorTypeUnspecified, nil, err - } - return multiFactorModel.MultiFactorWriteModel.MFAType, writeModelToObjectDetails(&multiFactorModel.WriteModel), nil -} - -func (c *Commands) addMultiFactorToDefaultLoginPolicy(ctx context.Context, instanceAgg *eventstore.Aggregate, multiFactorModel *InstanceMultiFactorWriteModel, multiFactor domain.MultiFactorType) (eventstore.Command, error) { - err := c.eventstore.FilterToQueryReducer(ctx, multiFactorModel) +func (c *Commands) AddMultiFactorToDefaultLoginPolicy(ctx context.Context, multiFactor domain.MultiFactorType) (*domain.ObjectDetails, error) { + instanceAgg := instance.NewAggregate(authz.GetInstance(ctx).InstanceID()) + cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, prepareAddMultiFactorToDefaultLoginPolicy(instanceAgg, multiFactor)) if err != nil { return nil, err } - if multiFactorModel.State == domain.FactorStateActive { - return nil, caos_errs.ThrowAlreadyExists(nil, "INSTANCE-3M9od", "Errors.IAM.LoginPolicy.MFA.AlreadyExists") + pushedEvents, err := c.eventstore.Push(ctx, cmds...) + if err != nil { + return nil, err } - - return instance.NewLoginPolicyMultiFactorAddedEvent(ctx, instanceAgg, multiFactor), nil + return pushedEventsToObjectDetails(pushedEvents), nil } func (c *Commands) RemoveMultiFactorFromDefaultLoginPolicy(ctx context.Context, multiFactor domain.MultiFactorType) (*domain.ObjectDetails, error) { @@ -336,3 +273,115 @@ func (c *Commands) defaultLoginPolicyWriteModelByID(ctx context.Context, writeMo } return nil } + +func (c *Commands) getDefaultLoginPolicy(ctx context.Context) (*domain.LoginPolicy, error) { + policyWriteModel := NewInstanceLoginPolicyWriteModel(ctx) + err := c.eventstore.FilterToQueryReducer(ctx, policyWriteModel) + if err != nil { + return nil, err + } + policy := writeModelToLoginPolicy(&policyWriteModel.LoginPolicyWriteModel) + policy.Default = true + return policy, nil +} + +func prepareAddDefaultLoginPolicy( + a *instance.Aggregate, + allowUsernamePassword bool, + allowRegister bool, + allowExternalIDP bool, + forceMFA bool, + hidePasswordReset bool, + ignoreUnknownUsernames bool, + passwordlessType domain.PasswordlessType, + defaultRedirectURI string, + passwordCheckLifetime time.Duration, + externalLoginCheckLifetime time.Duration, + mfaInitSkipLifetime time.Duration, + secondFactorCheckLifetime time.Duration, + multiFactorCheckLifetime time.Duration, +) preparation.Validation { + return func() (preparation.CreateCommands, error) { + return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) { + writeModel := NewInstanceLoginPolicyWriteModel(ctx) + events, err := filter(ctx, writeModel.Query()) + if err != nil { + return nil, err + } + writeModel.AppendEvents(events...) + if err = writeModel.Reduce(); err != nil { + return nil, err + } + if writeModel.State == domain.PolicyStateActive { + return nil, caos_errs.ThrowAlreadyExists(nil, "INSTANCE-2B0ps", "Errors.Instance.LoginPolicy.AlreadyExists") + } + return []eventstore.Command{ + instance.NewLoginPolicyAddedEvent(ctx, &a.Aggregate, + allowUsernamePassword, + allowRegister, + allowExternalIDP, + forceMFA, + hidePasswordReset, + ignoreUnknownUsernames, + passwordlessType, + defaultRedirectURI, + passwordCheckLifetime, + externalLoginCheckLifetime, + mfaInitSkipLifetime, + secondFactorCheckLifetime, + multiFactorCheckLifetime, + ), + }, nil + }, nil + } +} + +func prepareAddSecondFactorToDefaultLoginPolicy(a *instance.Aggregate, factor domain.SecondFactorType) preparation.Validation { + return func() (preparation.CreateCommands, error) { + if !factor.Valid() { + return nil, caos_errs.ThrowInvalidArgument(nil, "INSTANCE-5m9fs", "Errors.Instance.LoginPolicy.MFA.Unspecified") + } + return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) { + writeModel := NewInstanceSecondFactorWriteModel(ctx, factor) + events, err := filter(ctx, writeModel.Query()) + if err != nil { + return nil, err + } + writeModel.AppendEvents(events...) + if err = writeModel.Reduce(); err != nil { + return nil, err + } + if writeModel.State == domain.FactorStateActive { + return nil, caos_errs.ThrowAlreadyExists(nil, "INSTANCE-2B0ps", "Errors.Instance.MFA.AlreadyExists") + } + return []eventstore.Command{ + instance.NewLoginPolicySecondFactorAddedEvent(ctx, &a.Aggregate, factor), + }, nil + }, nil + } +} + +func prepareAddMultiFactorToDefaultLoginPolicy(a *instance.Aggregate, factor domain.MultiFactorType) preparation.Validation { + return func() (preparation.CreateCommands, error) { + if !factor.Valid() { + return nil, caos_errs.ThrowInvalidArgument(nil, "INSTANCE-5m9fs", "Errors.Instance.LoginPolicy.MFA.Unspecified") + } + return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) { + writeModel := NewInstanceMultiFactorWriteModel(ctx, factor) + events, err := filter(ctx, writeModel.Query()) + if err != nil { + return nil, err + } + writeModel.AppendEvents(events...) + if err = writeModel.Reduce(); err != nil { + return nil, err + } + if writeModel.State == domain.FactorStateActive { + return nil, caos_errs.ThrowAlreadyExists(nil, "INSTANCE-3M9od", "Errors.Instance.MFA.AlreadyExists") + } + return []eventstore.Command{ + instance.NewLoginPolicyMultiFactorAddedEvent(ctx, &a.Aggregate, factor), + }, nil + }, nil + } +} diff --git a/internal/command/instance_policy_login_test.go b/internal/command/instance_policy_login_test.go index 83b650745..956a204d2 100644 --- a/internal/command/instance_policy_login_test.go +++ b/internal/command/instance_policy_login_test.go @@ -23,11 +23,23 @@ func TestCommandSide_AddDefaultLoginPolicy(t *testing.T) { eventstore *eventstore.Eventstore } type args struct { - ctx context.Context - policy *domain.LoginPolicy + ctx context.Context + allowUsernamePassword bool + allowRegister bool + allowExternalIDP bool + forceMFA bool + hidePasswordReset bool + ignoreUnknownUsernames bool + passwordlessType domain.PasswordlessType + defaultRedirectURI string + passwordCheckLifetime time.Duration + externalLoginCheckLifetime time.Duration + mfaInitSkipLifetime time.Duration + secondFactorCheckLifetime time.Duration + multiFactorCheckLifetime time.Duration } type res struct { - want *domain.LoginPolicy + want *domain.ObjectDetails err func(error) bool } tests := []struct { @@ -64,12 +76,10 @@ func TestCommandSide_AddDefaultLoginPolicy(t *testing.T) { ), }, args: args{ - ctx: context.Background(), - policy: &domain.LoginPolicy{ - AllowRegister: true, - AllowUsernamePassword: true, - PasswordlessType: domain.PasswordlessTypeAllowed, - }, + ctx: context.Background(), + allowRegister: true, + allowUsernamePassword: true, + passwordlessType: domain.PasswordlessTypeAllowed, }, res: res{ err: caos_errs.IsErrorAlreadyExists, @@ -107,43 +117,24 @@ func TestCommandSide_AddDefaultLoginPolicy(t *testing.T) { ), }, args: args{ - ctx: authz.WithInstanceID(context.Background(), "INSTANCE"), - policy: &domain.LoginPolicy{ - AllowRegister: true, - AllowUsernamePassword: true, - AllowExternalIDP: true, - ForceMFA: true, - HidePasswordReset: true, - IgnoreUnknownUsernames: true, - PasswordlessType: domain.PasswordlessTypeAllowed, - DefaultRedirectURI: "https://example.com/redirect", - PasswordCheckLifetime: time.Hour * 1, - ExternalLoginCheckLifetime: time.Hour * 2, - MFAInitSkipLifetime: time.Hour * 3, - SecondFactorCheckLifetime: time.Hour * 4, - MultiFactorCheckLifetime: time.Hour * 5, - }, + ctx: authz.WithInstanceID(context.Background(), "INSTANCE"), + allowRegister: true, + allowUsernamePassword: true, + allowExternalIDP: true, + forceMFA: true, + hidePasswordReset: true, + ignoreUnknownUsernames: true, + passwordlessType: domain.PasswordlessTypeAllowed, + defaultRedirectURI: "https://example.com/redirect", + passwordCheckLifetime: time.Hour * 1, + externalLoginCheckLifetime: time.Hour * 2, + mfaInitSkipLifetime: time.Hour * 3, + secondFactorCheckLifetime: time.Hour * 4, + multiFactorCheckLifetime: time.Hour * 5, }, res: res{ - want: &domain.LoginPolicy{ - ObjectRoot: models.ObjectRoot{ - InstanceID: "INSTANCE", - AggregateID: "INSTANCE", - ResourceOwner: "INSTANCE", - }, - AllowRegister: true, - AllowUsernamePassword: true, - AllowExternalIDP: true, - ForceMFA: true, - HidePasswordReset: true, - IgnoreUnknownUsernames: true, - PasswordlessType: domain.PasswordlessTypeAllowed, - DefaultRedirectURI: "https://example.com/redirect", - PasswordCheckLifetime: time.Hour * 1, - ExternalLoginCheckLifetime: time.Hour * 2, - MFAInitSkipLifetime: time.Hour * 3, - SecondFactorCheckLifetime: time.Hour * 4, - MultiFactorCheckLifetime: time.Hour * 5, + want: &domain.ObjectDetails{ + ResourceOwner: "INSTANCE", }, }, }, @@ -153,7 +144,22 @@ func TestCommandSide_AddDefaultLoginPolicy(t *testing.T) { r := &Commands{ eventstore: tt.fields.eventstore, } - got, err := r.AddDefaultLoginPolicy(tt.args.ctx, tt.args.policy) + got, err := r.AddDefaultLoginPolicy( + tt.args.ctx, + tt.args.allowUsernamePassword, + tt.args.allowRegister, + tt.args.allowExternalIDP, + tt.args.forceMFA, + tt.args.hidePasswordReset, + tt.args.ignoreUnknownUsernames, + tt.args.passwordlessType, + tt.args.defaultRedirectURI, + tt.args.passwordCheckLifetime, + tt.args.externalLoginCheckLifetime, + tt.args.mfaInitSkipLifetime, + tt.args.secondFactorCheckLifetime, + tt.args.multiFactorCheckLifetime, + ) if tt.res.err == nil { assert.NoError(t, err) } @@ -1046,7 +1052,7 @@ func TestCommandSide_AddSecondFactorDefaultLoginPolicy(t *testing.T) { r := &Commands{ eventstore: tt.fields.eventstore, } - _, got, err := r.AddSecondFactorToDefaultLoginPolicy(tt.args.ctx, tt.args.factor) + got, err := r.AddSecondFactorToDefaultLoginPolicy(tt.args.ctx, tt.args.factor) if tt.res.err == nil { assert.NoError(t, err) } @@ -1282,7 +1288,7 @@ func TestCommandSide_AddMultiFactorDefaultLoginPolicy(t *testing.T) { r := &Commands{ eventstore: tt.fields.eventstore, } - _, got, err := r.AddMultiFactorToDefaultLoginPolicy(tt.args.ctx, tt.args.factor) + got, err := r.AddMultiFactorToDefaultLoginPolicy(tt.args.ctx, tt.args.factor) if tt.res.err == nil { assert.NoError(t, err) } diff --git a/internal/command/instance_policy_mail_template.go b/internal/command/instance_policy_mail_template.go index 283c9e44a..78ee0ec27 100644 --- a/internal/command/instance_policy_mail_template.go +++ b/internal/command/instance_policy_mail_template.go @@ -3,6 +3,7 @@ package command import ( "context" + "github.com/zitadel/zitadel/internal/command/preparation" "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" @@ -93,3 +94,33 @@ func (c *Commands) defaultMailTemplateWriteModelByID(ctx context.Context) (polic } return writeModel, nil } + +func prepareAddDefaultEmailTemplate( + a *instance.Aggregate, + template []byte, +) preparation.Validation { + return func() (preparation.CreateCommands, error) { + if template == nil { + return nil, caos_errs.ThrowInvalidArgument(nil, "INSTANCE-fm9sd", "Errors.Instance.MailTemplate.Invalid") + } + return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) { + writeModel := NewInstanceMailTemplateWriteModel(ctx) + events, err := filter(ctx, writeModel.Query()) + if err != nil { + return nil, err + } + writeModel.AppendEvents(events...) + if err = writeModel.Reduce(); err != nil { + return nil, err + } + if writeModel.State == domain.PolicyStateActive { + return nil, caos_errs.ThrowAlreadyExists(nil, "INSTANCE-5n8fs", "Errors.Instance.MailTemplate.AlreadyExists") + } + return []eventstore.Command{ + instance.NewMailTemplateAddedEvent(ctx, &a.Aggregate, + template, + ), + }, nil + }, nil + } +} diff --git a/internal/command/instance_policy_password_age.go b/internal/command/instance_policy_password_age.go index 85b31343c..e1044c269 100644 --- a/internal/command/instance_policy_password_age.go +++ b/internal/command/instance_policy_password_age.go @@ -3,6 +3,8 @@ package command import ( "context" + "github.com/zitadel/zitadel/internal/api/authz" + "github.com/zitadel/zitadel/internal/command/preparation" "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" @@ -10,36 +12,17 @@ import ( "github.com/zitadel/zitadel/internal/telemetry/tracing" ) -func (c *Commands) AddDefaultPasswordAgePolicy(ctx context.Context, policy *domain.PasswordAgePolicy) (*domain.PasswordAgePolicy, error) { - addedPolicy := NewInstancePasswordAgePolicyWriteModel(ctx) - instanceAgg := InstanceAggregateFromWriteModel(&addedPolicy.WriteModel) - event, err := c.addDefaultPasswordAgePolicy(ctx, instanceAgg, addedPolicy, policy) +func (c *Commands) AddDefaultPasswordAgePolicy(ctx context.Context, expireWarnDays, maxAgeDays uint64) (*domain.ObjectDetails, error) { + instanceAgg := instance.NewAggregate(authz.GetInstance(ctx).InstanceID()) + cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, prepareAddDefaultPasswordAgePolicy(instanceAgg, expireWarnDays, maxAgeDays)) if err != nil { return nil, err } - - pushedEvents, err := c.eventstore.Push(ctx, event) + pushedEvents, err := c.eventstore.Push(ctx, cmds...) if err != nil { return nil, err } - err = AppendAndReduce(addedPolicy, pushedEvents...) - if err != nil { - return nil, err - } - return writeModelToPasswordAgePolicy(&addedPolicy.PasswordAgePolicyWriteModel), nil -} - -func (c *Commands) addDefaultPasswordAgePolicy(ctx context.Context, instanceAgg *eventstore.Aggregate, addedPolicy *InstancePasswordAgePolicyWriteModel, policy *domain.PasswordAgePolicy) (eventstore.Command, error) { - err := c.eventstore.FilterToQueryReducer(ctx, addedPolicy) - if err != nil { - return nil, err - } - if addedPolicy.State == domain.PolicyStateActive { - return nil, caos_errs.ThrowAlreadyExists(nil, "INSTANCE-Lk0dS", "Errors.IAM.PasswordAgePolicy.AlreadyExists") - } - - return instance.NewPasswordAgePolicyAddedEvent(ctx, instanceAgg, policy.ExpireWarnDays, policy.MaxAgeDays), nil - + return pushedEventsToObjectDetails(pushedEvents), nil } func (c *Commands) ChangeDefaultPasswordAgePolicy(ctx context.Context, policy *domain.PasswordAgePolicy) (*domain.PasswordAgePolicy, error) { @@ -80,3 +63,32 @@ func (c *Commands) defaultPasswordAgePolicyWriteModelByID(ctx context.Context) ( } return writeModel, nil } + +func prepareAddDefaultPasswordAgePolicy( + a *instance.Aggregate, + expireWarnDays, + maxAgeDays uint64, +) preparation.Validation { + return func() (preparation.CreateCommands, error) { + return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) { + writeModel := NewInstancePasswordAgePolicyWriteModel(ctx) + events, err := filter(ctx, writeModel.Query()) + if err != nil { + return nil, err + } + writeModel.AppendEvents(events...) + if err = writeModel.Reduce(); err != nil { + return nil, err + } + if writeModel.State == domain.PolicyStateActive { + return nil, caos_errs.ThrowAlreadyExists(nil, "INSTANCE-Lk0dS", "Errors.Instance.PasswordAgePolicy.AlreadyExists") + } + return []eventstore.Command{ + instance.NewPasswordAgePolicyAddedEvent(ctx, &a.Aggregate, + expireWarnDays, + maxAgeDays, + ), + }, nil + }, nil + } +} diff --git a/internal/command/instance_policy_password_age_test.go b/internal/command/instance_policy_password_age_test.go index ff3753456..ac6f9bb51 100644 --- a/internal/command/instance_policy_password_age_test.go +++ b/internal/command/instance_policy_password_age_test.go @@ -21,11 +21,12 @@ func TestCommandSide_AddDefaultPasswordAgePolicy(t *testing.T) { eventstore *eventstore.Eventstore } type args struct { - ctx context.Context - policy *domain.PasswordAgePolicy + ctx context.Context + maxAgeDays uint64 + expireWarnDays uint64 } type res struct { - want *domain.PasswordAgePolicy + want *domain.ObjectDetails err func(error) bool } tests := []struct { @@ -51,11 +52,9 @@ func TestCommandSide_AddDefaultPasswordAgePolicy(t *testing.T) { ), }, args: args{ - ctx: context.Background(), - policy: &domain.PasswordAgePolicy{ - MaxAgeDays: 365, - ExpireWarnDays: 10, - }, + ctx: context.Background(), + maxAgeDays: 365, + expireWarnDays: 10, }, res: res{ err: caos_errs.IsErrorAlreadyExists, @@ -82,21 +81,13 @@ func TestCommandSide_AddDefaultPasswordAgePolicy(t *testing.T) { ), }, args: args{ - ctx: authz.WithInstanceID(context.Background(), "INSTANCE"), - policy: &domain.PasswordAgePolicy{ - ExpireWarnDays: 365, - MaxAgeDays: 10, - }, + ctx: authz.WithInstanceID(context.Background(), "INSTANCE"), + expireWarnDays: 365, + maxAgeDays: 10, }, res: res{ - want: &domain.PasswordAgePolicy{ - ObjectRoot: models.ObjectRoot{ - InstanceID: "INSTANCE", - AggregateID: "INSTANCE", - ResourceOwner: "INSTANCE", - }, - ExpireWarnDays: 365, - MaxAgeDays: 10, + want: &domain.ObjectDetails{ + ResourceOwner: "INSTANCE", }, }, }, @@ -106,7 +97,7 @@ func TestCommandSide_AddDefaultPasswordAgePolicy(t *testing.T) { r := &Commands{ eventstore: tt.fields.eventstore, } - got, err := r.AddDefaultPasswordAgePolicy(tt.args.ctx, tt.args.policy) + got, err := r.AddDefaultPasswordAgePolicy(tt.args.ctx, tt.args.expireWarnDays, tt.args.maxAgeDays) if tt.res.err == nil { assert.NoError(t, err) } diff --git a/internal/command/instance_policy_password_complexity.go b/internal/command/instance_policy_password_complexity.go index d7859ffeb..c2af1064b 100644 --- a/internal/command/instance_policy_password_complexity.go +++ b/internal/command/instance_policy_password_complexity.go @@ -3,6 +3,8 @@ package command import ( "context" + "github.com/zitadel/zitadel/internal/api/authz" + "github.com/zitadel/zitadel/internal/command/preparation" "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" @@ -10,53 +12,17 @@ import ( "github.com/zitadel/zitadel/internal/telemetry/tracing" ) -func (c *Commands) getDefaultPasswordComplexityPolicy(ctx context.Context) (*domain.PasswordComplexityPolicy, error) { - policyWriteModel := NewInstancePasswordComplexityPolicyWriteModel(ctx) - err := c.eventstore.FilterToQueryReducer(ctx, policyWriteModel) +func (c *Commands) AddDefaultPasswordComplexityPolicy(ctx context.Context, minLength uint64, hasLowercase, hasUppercase, hasNumber, hasSymbol bool) (*domain.ObjectDetails, error) { + instanceAgg := instance.NewAggregate(authz.GetInstance(ctx).InstanceID()) + cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, prepareAddDefaultPasswordComplexityPolicy(instanceAgg, minLength, hasLowercase, hasUppercase, hasNumber, hasSymbol)) if err != nil { return nil, err } - if !policyWriteModel.State.Exists() { - return nil, caos_errs.ThrowInvalidArgument(nil, "INSTANCE-M0gsf", "Errors.IAM.PasswordComplexityPolicy.NotFound") - } - policy := writeModelToPasswordComplexityPolicy(&policyWriteModel.PasswordComplexityPolicyWriteModel) - policy.Default = true - return policy, nil -} - -func (c *Commands) AddDefaultPasswordComplexityPolicy(ctx context.Context, policy *domain.PasswordComplexityPolicy) (*domain.PasswordComplexityPolicy, error) { - addedPolicy := NewInstancePasswordComplexityPolicyWriteModel(ctx) - instanceAgg := InstanceAggregateFromWriteModel(&addedPolicy.WriteModel) - events, err := c.addDefaultPasswordComplexityPolicy(ctx, instanceAgg, addedPolicy, policy) + pushedEvents, err := c.eventstore.Push(ctx, cmds...) if err != nil { return nil, err } - - pushedEvents, err := c.eventstore.Push(ctx, events) - if err != nil { - return nil, err - } - err = AppendAndReduce(addedPolicy, pushedEvents...) - if err != nil { - return nil, err - } - return writeModelToPasswordComplexityPolicy(&addedPolicy.PasswordComplexityPolicyWriteModel), nil -} - -func (c *Commands) addDefaultPasswordComplexityPolicy(ctx context.Context, instanceAgg *eventstore.Aggregate, addedPolicy *InstancePasswordComplexityPolicyWriteModel, policy *domain.PasswordComplexityPolicy) (eventstore.Command, error) { - if err := policy.IsValid(); err != nil { - return nil, err - } - - err := c.eventstore.FilterToQueryReducer(ctx, addedPolicy) - if err != nil { - return nil, err - } - if addedPolicy.State == domain.PolicyStateActive { - return nil, caos_errs.ThrowAlreadyExists(nil, "INSTANCE-Lk0dS", "Errors.IAM.PasswordComplexityPolicy.AlreadyExists") - } - - return instance.NewPasswordComplexityPolicyAddedEvent(ctx, instanceAgg, policy.MinLength, policy.HasLowercase, policy.HasUppercase, policy.HasNumber, policy.HasSymbol), nil + return pushedEventsToObjectDetails(pushedEvents), nil } func (c *Commands) ChangeDefaultPasswordComplexityPolicy(ctx context.Context, policy *domain.PasswordComplexityPolicy) (*domain.PasswordComplexityPolicy, error) { @@ -88,6 +54,58 @@ func (c *Commands) ChangeDefaultPasswordComplexityPolicy(ctx context.Context, po return writeModelToPasswordComplexityPolicy(&existingPolicy.PasswordComplexityPolicyWriteModel), nil } +func prepareAddDefaultPasswordComplexityPolicy( + a *instance.Aggregate, + minLength uint64, + hasLowercase, + hasUppercase, + hasNumber, + hasSymbol bool, +) preparation.Validation { + return func() (preparation.CreateCommands, error) { + if minLength == 0 || minLength > 72 { + return nil, caos_errs.ThrowInvalidArgument(nil, "INSTANCE-Lsp0e", "Errors.Instance.PasswordComplexityPolicy.MinLengthNotAllowed") + } + return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) { + writeModel := NewInstancePasswordComplexityPolicyWriteModel(ctx) + events, err := filter(ctx, writeModel.Query()) + if err != nil { + return nil, err + } + writeModel.AppendEvents(events...) + if err = writeModel.Reduce(); err != nil { + return nil, err + } + if writeModel.State == domain.PolicyStateActive { + return nil, caos_errs.ThrowAlreadyExists(nil, "INSTANCE-Lk0dS", "Errors.Instance.PasswordComplexityPolicy.AlreadyExists") + } + return []eventstore.Command{ + instance.NewPasswordComplexityPolicyAddedEvent(ctx, &a.Aggregate, + minLength, + hasLowercase, + hasUppercase, + hasNumber, + hasSymbol, + ), + }, nil + }, nil + } +} + +func (c *Commands) getDefaultPasswordComplexityPolicy(ctx context.Context) (*domain.PasswordComplexityPolicy, error) { + policyWriteModel := NewInstancePasswordComplexityPolicyWriteModel(ctx) + err := c.eventstore.FilterToQueryReducer(ctx, policyWriteModel) + if err != nil { + return nil, err + } + if !policyWriteModel.State.Exists() { + return nil, caos_errs.ThrowInvalidArgument(nil, "INSTANCE-M0gsf", "Errors.IAM.PasswordComplexityPolicy.NotFound") + } + policy := writeModelToPasswordComplexityPolicy(&policyWriteModel.PasswordComplexityPolicyWriteModel) + policy.Default = true + return policy, nil +} + func (c *Commands) defaultPasswordComplexityPolicyWriteModelByID(ctx context.Context) (policy *InstancePasswordComplexityPolicyWriteModel, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() diff --git a/internal/command/instance_policy_password_complexity_test.go b/internal/command/instance_policy_password_complexity_test.go index 67bf8038e..3e342061b 100644 --- a/internal/command/instance_policy_password_complexity_test.go +++ b/internal/command/instance_policy_password_complexity_test.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" @@ -20,11 +21,15 @@ func TestCommandSide_AddDefaultPasswordComplexityPolicy(t *testing.T) { eventstore *eventstore.Eventstore } type args struct { - ctx context.Context - policy *domain.PasswordComplexityPolicy + ctx context.Context + minLength uint64 + hasLowercase bool + hasUppercase bool + hasNumber bool + hasSymbol bool } type res struct { - want *domain.PasswordComplexityPolicy + want *domain.ObjectDetails err func(error) bool } tests := []struct { @@ -41,14 +46,12 @@ func TestCommandSide_AddDefaultPasswordComplexityPolicy(t *testing.T) { ), }, args: args{ - ctx: context.Background(), - policy: &domain.PasswordComplexityPolicy{ - MinLength: 0, - HasUppercase: true, - HasLowercase: true, - HasNumber: true, - HasSymbol: true, - }, + ctx: context.Background(), + minLength: 0, + hasUppercase: true, + hasLowercase: true, + hasNumber: true, + hasSymbol: true, }, res: res{ err: caos_errs.IsErrorInvalidArgument, @@ -71,14 +74,12 @@ func TestCommandSide_AddDefaultPasswordComplexityPolicy(t *testing.T) { ), }, args: args{ - ctx: authz.WithInstanceID(context.Background(), "INSTANCE"), - policy: &domain.PasswordComplexityPolicy{ - MinLength: 8, - HasUppercase: true, - HasLowercase: true, - HasNumber: true, - HasSymbol: true, - }, + ctx: authz.WithInstanceID(context.Background(), "INSTANCE"), + minLength: 8, + hasUppercase: true, + hasLowercase: true, + hasNumber: true, + hasSymbol: true, }, res: res{ err: caos_errs.IsErrorAlreadyExists, @@ -105,27 +106,16 @@ func TestCommandSide_AddDefaultPasswordComplexityPolicy(t *testing.T) { ), }, args: args{ - ctx: authz.WithInstanceID(context.Background(), "INSTANCE"), - policy: &domain.PasswordComplexityPolicy{ - MinLength: 8, - HasUppercase: true, - HasLowercase: true, - HasNumber: true, - HasSymbol: true, - }, + ctx: authz.WithInstanceID(context.Background(), "INSTANCE"), + minLength: 8, + hasUppercase: true, + hasLowercase: true, + hasNumber: true, + hasSymbol: true, }, res: res{ - want: &domain.PasswordComplexityPolicy{ - ObjectRoot: models.ObjectRoot{ - InstanceID: "INSTANCE", - AggregateID: "INSTANCE", - ResourceOwner: "INSTANCE", - }, - MinLength: 8, - HasUppercase: true, - HasLowercase: true, - HasNumber: true, - HasSymbol: true, + want: &domain.ObjectDetails{ + ResourceOwner: "INSTANCE", }, }, }, @@ -135,7 +125,7 @@ func TestCommandSide_AddDefaultPasswordComplexityPolicy(t *testing.T) { r := &Commands{ eventstore: tt.fields.eventstore, } - got, err := r.AddDefaultPasswordComplexityPolicy(tt.args.ctx, tt.args.policy) + got, err := r.AddDefaultPasswordComplexityPolicy(tt.args.ctx, tt.args.minLength, tt.args.hasLowercase, tt.args.hasUppercase, tt.args.hasNumber, tt.args.hasSymbol) if tt.res.err == nil { assert.NoError(t, err) } diff --git a/internal/command/instance_policy_password_lockout.go b/internal/command/instance_policy_password_lockout.go index f9c048887..5b0b95949 100644 --- a/internal/command/instance_policy_password_lockout.go +++ b/internal/command/instance_policy_password_lockout.go @@ -3,6 +3,8 @@ package command import ( "context" + "github.com/zitadel/zitadel/internal/api/authz" + "github.com/zitadel/zitadel/internal/command/preparation" "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" @@ -10,35 +12,17 @@ import ( "github.com/zitadel/zitadel/internal/telemetry/tracing" ) -func (c *Commands) AddDefaultLockoutPolicy(ctx context.Context, policy *domain.LockoutPolicy) (*domain.LockoutPolicy, error) { - addedPolicy := NewInstanceLockoutPolicyWriteModel(ctx) - instanceAgg := InstanceAggregateFromWriteModel(&addedPolicy.WriteModel) - event, err := c.addDefaultLockoutPolicy(ctx, instanceAgg, addedPolicy, policy) +func (c *Commands) AddDefaultLockoutPolicy(ctx context.Context, maxAttempts uint64, showLockoutFailure bool) (*domain.ObjectDetails, error) { + instanceAgg := instance.NewAggregate(authz.GetInstance(ctx).InstanceID()) + cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, prepareAddDefaultLockoutPolicy(instanceAgg, maxAttempts, showLockoutFailure)) if err != nil { return nil, err } - pushedEvents, err := c.eventstore.Push(ctx, event) + pushedEvents, err := c.eventstore.Push(ctx, cmds...) if err != nil { return nil, err } - err = AppendAndReduce(addedPolicy, pushedEvents...) - if err != nil { - return nil, err - } - - return writeModelToLockoutPolicy(&addedPolicy.LockoutPolicyWriteModel), nil -} - -func (c *Commands) addDefaultLockoutPolicy(ctx context.Context, instanceAgg *eventstore.Aggregate, addedPolicy *InstanceLockoutPolicyWriteModel, policy *domain.LockoutPolicy) (eventstore.Command, error) { - err := c.eventstore.FilterToQueryReducer(ctx, addedPolicy) - if err != nil { - return nil, err - } - if addedPolicy.State == domain.PolicyStateActive { - return nil, caos_errs.ThrowAlreadyExists(nil, "INSTANCE-0olDf", "Errors.IAM.LockoutPolicy.AlreadyExists") - } - - return instance.NewLockoutPolicyAddedEvent(ctx, instanceAgg, policy.MaxPasswordAttempts, policy.ShowLockOutFailures), nil + return pushedEventsToObjectDetails(pushedEvents), nil } func (c *Commands) ChangeDefaultLockoutPolicy(ctx context.Context, policy *domain.LockoutPolicy) (*domain.LockoutPolicy, error) { @@ -78,3 +62,29 @@ func (c *Commands) defaultLockoutPolicyWriteModelByID(ctx context.Context) (poli } return writeModel, nil } + +func prepareAddDefaultLockoutPolicy( + a *instance.Aggregate, + maxAttempts uint64, + showLockoutFailure bool, +) preparation.Validation { + return func() (preparation.CreateCommands, error) { + return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) { + writeModel := NewInstanceLockoutPolicyWriteModel(ctx) + events, err := filter(ctx, writeModel.Query()) + if err != nil { + return nil, err + } + writeModel.AppendEvents(events...) + if err = writeModel.Reduce(); err != nil { + return nil, err + } + if writeModel.State == domain.PolicyStateActive { + return nil, caos_errs.ThrowAlreadyExists(nil, "INSTANCE-0olDf", "Errors.Instance.LockoutPolicy.AlreadyExists") + } + return []eventstore.Command{ + instance.NewLockoutPolicyAddedEvent(ctx, &a.Aggregate, maxAttempts, showLockoutFailure), + }, nil + }, nil + } +} diff --git a/internal/command/instance_policy_password_lockout_test.go b/internal/command/instance_policy_password_lockout_test.go index 537d2a93e..07a0c77cf 100644 --- a/internal/command/instance_policy_password_lockout_test.go +++ b/internal/command/instance_policy_password_lockout_test.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" @@ -20,11 +21,12 @@ func TestCommandSide_AddDefaultLockoutPolicy(t *testing.T) { eventstore *eventstore.Eventstore } type args struct { - ctx context.Context - policy *domain.LockoutPolicy + ctx context.Context + maxPasswordAttempts uint64 + showLockOutFailures bool } type res struct { - want *domain.LockoutPolicy + want *domain.ObjectDetails err func(error) bool } tests := []struct { @@ -50,11 +52,9 @@ func TestCommandSide_AddDefaultLockoutPolicy(t *testing.T) { ), }, args: args{ - ctx: context.Background(), - policy: &domain.LockoutPolicy{ - MaxPasswordAttempts: 10, - ShowLockOutFailures: true, - }, + ctx: context.Background(), + maxPasswordAttempts: 10, + showLockOutFailures: true, }, res: res{ err: caos_errs.IsErrorAlreadyExists, @@ -81,21 +81,13 @@ func TestCommandSide_AddDefaultLockoutPolicy(t *testing.T) { ), }, args: args{ - ctx: authz.WithInstanceID(context.Background(), "INSTANCE"), - policy: &domain.LockoutPolicy{ - MaxPasswordAttempts: 10, - ShowLockOutFailures: true, - }, + ctx: authz.WithInstanceID(context.Background(), "INSTANCE"), + maxPasswordAttempts: 10, + showLockOutFailures: true, }, res: res{ - want: &domain.LockoutPolicy{ - ObjectRoot: models.ObjectRoot{ - InstanceID: "INSTANCE", - AggregateID: "INSTANCE", - ResourceOwner: "INSTANCE", - }, - MaxPasswordAttempts: 10, - ShowLockOutFailures: true, + want: &domain.ObjectDetails{ + ResourceOwner: "INSTANCE", }, }, }, @@ -105,7 +97,7 @@ func TestCommandSide_AddDefaultLockoutPolicy(t *testing.T) { r := &Commands{ eventstore: tt.fields.eventstore, } - got, err := r.AddDefaultLockoutPolicy(tt.args.ctx, tt.args.policy) + got, err := r.AddDefaultLockoutPolicy(tt.args.ctx, tt.args.maxPasswordAttempts, tt.args.showLockOutFailures) if tt.res.err == nil { assert.NoError(t, err) } diff --git a/internal/command/instance_policy_privacy.go b/internal/command/instance_policy_privacy.go index 071c6676f..141b32765 100644 --- a/internal/command/instance_policy_privacy.go +++ b/internal/command/instance_policy_privacy.go @@ -3,6 +3,8 @@ package command import ( "context" + "github.com/zitadel/zitadel/internal/api/authz" + "github.com/zitadel/zitadel/internal/command/preparation" "github.com/zitadel/zitadel/internal/domain" caos_errs "github.com/zitadel/zitadel/internal/errors" "github.com/zitadel/zitadel/internal/eventstore" @@ -10,49 +12,17 @@ import ( "github.com/zitadel/zitadel/internal/telemetry/tracing" ) -func (c *Commands) getDefaultPrivacyPolicy(ctx context.Context) (*domain.PrivacyPolicy, error) { - policyWriteModel := NewInstancePrivacyPolicyWriteModel(ctx) - err := c.eventstore.FilterToQueryReducer(ctx, policyWriteModel) +func (c *Commands) AddDefaultPrivacyPolicy(ctx context.Context, tosLink, privacyLink, helpLink string) (*domain.ObjectDetails, error) { + instanceAgg := instance.NewAggregate(authz.GetInstance(ctx).InstanceID()) + cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, prepareAddDefaultPrivacyPolicy(instanceAgg, tosLink, privacyLink, helpLink)) if err != nil { return nil, err } - if !policyWriteModel.State.Exists() { - return nil, caos_errs.ThrowInvalidArgument(nil, "INSTANCE-559os", "Errors.IAM.PrivacyPolicy.NotFound") - } - policy := writeModelToPrivacyPolicy(&policyWriteModel.PrivacyPolicyWriteModel) - policy.Default = true - return policy, nil -} - -func (c *Commands) AddDefaultPrivacyPolicy(ctx context.Context, policy *domain.PrivacyPolicy) (*domain.PrivacyPolicy, error) { - addedPolicy := NewInstancePrivacyPolicyWriteModel(ctx) - instanceAgg := InstanceAggregateFromWriteModel(&addedPolicy.WriteModel) - events, err := c.addDefaultPrivacyPolicy(ctx, instanceAgg, addedPolicy, policy) + pushedEvents, err := c.eventstore.Push(ctx, cmds...) if err != nil { return nil, err } - - pushedEvents, err := c.eventstore.Push(ctx, events) - if err != nil { - return nil, err - } - err = AppendAndReduce(addedPolicy, pushedEvents...) - if err != nil { - return nil, err - } - return writeModelToPrivacyPolicy(&addedPolicy.PrivacyPolicyWriteModel), nil -} - -func (c *Commands) addDefaultPrivacyPolicy(ctx context.Context, instanceAgg *eventstore.Aggregate, addedPolicy *InstancePrivacyPolicyWriteModel, policy *domain.PrivacyPolicy) (eventstore.Command, error) { - err := c.eventstore.FilterToQueryReducer(ctx, addedPolicy) - if err != nil { - return nil, err - } - if addedPolicy.State == domain.PolicyStateActive { - return nil, caos_errs.ThrowAlreadyExists(nil, "INSTANCE-M00rJ", "Errors.IAM.PrivacyPolicy.AlreadyExists") - } - - return instance.NewPrivacyPolicyAddedEvent(ctx, instanceAgg, policy.TOSLink, policy.PrivacyLink, policy.HelpLink), nil + return pushedEventsToObjectDetails(pushedEvents), nil } func (c *Commands) ChangeDefaultPrivacyPolicy(ctx context.Context, policy *domain.PrivacyPolicy) (*domain.PrivacyPolicy, error) { @@ -91,3 +61,44 @@ func (c *Commands) defaultPrivacyPolicyWriteModelByID(ctx context.Context) (poli } return writeModel, nil } + +func (c *Commands) getDefaultPrivacyPolicy(ctx context.Context) (*domain.PrivacyPolicy, error) { + policyWriteModel := NewInstancePrivacyPolicyWriteModel(ctx) + err := c.eventstore.FilterToQueryReducer(ctx, policyWriteModel) + if err != nil { + return nil, err + } + if !policyWriteModel.State.Exists() { + return nil, caos_errs.ThrowInvalidArgument(nil, "INSTANCE-559os", "Errors.IAM.PrivacyPolicy.NotFound") + } + policy := writeModelToPrivacyPolicy(&policyWriteModel.PrivacyPolicyWriteModel) + policy.Default = true + return policy, nil +} + +func prepareAddDefaultPrivacyPolicy( + a *instance.Aggregate, + tosLink, + privacyLink, + helpLink string, +) preparation.Validation { + return func() (preparation.CreateCommands, error) { + return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) { + writeModel := NewInstancePrivacyPolicyWriteModel(ctx) + events, err := filter(ctx, writeModel.Query()) + if err != nil { + return nil, err + } + writeModel.AppendEvents(events...) + if err = writeModel.Reduce(); err != nil { + return nil, err + } + if writeModel.State == domain.PolicyStateActive { + return nil, caos_errs.ThrowAlreadyExists(nil, "INSTANCE-M00rJ", "Errors.Instance.PrivacyPolicy.AlreadyExists") + } + return []eventstore.Command{ + instance.NewPrivacyPolicyAddedEvent(ctx, &a.Aggregate, tosLink, privacyLink, helpLink), + }, nil + }, nil + } +} diff --git a/internal/command/instance_policy_privacy_test.go b/internal/command/instance_policy_privacy_test.go index 38ad8e361..707afa830 100644 --- a/internal/command/instance_policy_privacy_test.go +++ b/internal/command/instance_policy_privacy_test.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/stretchr/testify/assert" + "github.com/zitadel/zitadel/internal/api/authz" "github.com/zitadel/zitadel/internal/domain" @@ -21,11 +22,13 @@ func TestCommandSide_AddDefaultPrivacyPolicy(t *testing.T) { eventstore *eventstore.Eventstore } type args struct { - ctx context.Context - policy *domain.PrivacyPolicy + ctx context.Context + tosLink string + privacyLink string + helpLink string } type res struct { - want *domain.PrivacyPolicy + want *domain.ObjectDetails err func(error) bool } tests := []struct { @@ -52,12 +55,10 @@ func TestCommandSide_AddDefaultPrivacyPolicy(t *testing.T) { ), }, args: args{ - ctx: context.Background(), - policy: &domain.PrivacyPolicy{ - TOSLink: "TOSLink", - PrivacyLink: "PrivacyLink", - HelpLink: "HelpLink", - }, + ctx: context.Background(), + tosLink: "TOSLink", + privacyLink: "PrivacyLink", + helpLink: "HelpLink", }, res: res{ err: caos_errs.IsErrorAlreadyExists, @@ -85,23 +86,14 @@ func TestCommandSide_AddDefaultPrivacyPolicy(t *testing.T) { ), }, args: args{ - ctx: authz.WithInstanceID(context.Background(), "INSTANCE"), - policy: &domain.PrivacyPolicy{ - TOSLink: "TOSLink", - PrivacyLink: "PrivacyLink", - HelpLink: "HelpLink", - }, + ctx: authz.WithInstanceID(context.Background(), "INSTANCE"), + tosLink: "TOSLink", + privacyLink: "PrivacyLink", + helpLink: "HelpLink", }, res: res{ - want: &domain.PrivacyPolicy{ - ObjectRoot: models.ObjectRoot{ - InstanceID: "INSTANCE", - AggregateID: "INSTANCE", - ResourceOwner: "INSTANCE", - }, - TOSLink: "TOSLink", - PrivacyLink: "PrivacyLink", - HelpLink: "HelpLink", + want: &domain.ObjectDetails{ + ResourceOwner: "INSTANCE", }, }, }, @@ -127,23 +119,14 @@ func TestCommandSide_AddDefaultPrivacyPolicy(t *testing.T) { ), }, args: args{ - ctx: authz.WithInstanceID(context.Background(), "INSTANCE"), - policy: &domain.PrivacyPolicy{ - TOSLink: "", - PrivacyLink: "", - HelpLink: "", - }, + ctx: authz.WithInstanceID(context.Background(), "INSTANCE"), + tosLink: "", + privacyLink: "", + helpLink: "", }, res: res{ - want: &domain.PrivacyPolicy{ - ObjectRoot: models.ObjectRoot{ - InstanceID: "INSTANCE", - AggregateID: "INSTANCE", - ResourceOwner: "INSTANCE", - }, - TOSLink: "", - PrivacyLink: "", - HelpLink: "", + want: &domain.ObjectDetails{ + ResourceOwner: "INSTANCE", }, }, }, @@ -153,7 +136,7 @@ func TestCommandSide_AddDefaultPrivacyPolicy(t *testing.T) { r := &Commands{ eventstore: tt.fields.eventstore, } - got, err := r.AddDefaultPrivacyPolicy(tt.args.ctx, tt.args.policy) + got, err := r.AddDefaultPrivacyPolicy(tt.args.ctx, tt.args.tosLink, tt.args.privacyLink, tt.args.helpLink) if tt.res.err == nil { assert.NoError(t, err) } diff --git a/internal/command/instance_privacy_policy.go b/internal/command/instance_privacy_policy.go deleted file mode 100644 index cd1ef0d95..000000000 --- a/internal/command/instance_privacy_policy.go +++ /dev/null @@ -1,25 +0,0 @@ -package command - -import ( - "context" - - "github.com/zitadel/zitadel/internal/command/preparation" - "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/repository/instance" -) - -func AddPrivacyPolicy( - a *instance.Aggregate, - tosLink, - privacyLink, - helpLink string, -) preparation.Validation { - return func() (preparation.CreateCommands, error) { - return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) { - //TODO: check if already exists - return []eventstore.Command{ - instance.NewPrivacyPolicyAddedEvent(ctx, &a.Aggregate, tosLink, privacyLink, helpLink), - }, nil - }, nil - } -} diff --git a/internal/command/instance_settings.go b/internal/command/instance_settings.go index 9389f82ba..3f95f91a0 100644 --- a/internal/command/instance_settings.go +++ b/internal/command/instance_settings.go @@ -15,7 +15,7 @@ import ( func (c *Commands) AddSecretGeneratorConfig(ctx context.Context, typ domain.SecretGeneratorType, config *crypto.GeneratorConfig) (*domain.ObjectDetails, error) { agg := instance.NewAggregate(authz.GetInstance(ctx).InstanceID()) - cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, addSecretGeneratorConfig(agg, typ, config)) + cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, prepareAddSecretGeneratorConfig(agg, typ, config)) if err != nil { return nil, err } @@ -31,7 +31,7 @@ func (c *Commands) AddSecretGeneratorConfig(ctx context.Context, typ domain.Secr }, nil } -func addSecretGeneratorConfig(a *instance.Aggregate, typ domain.SecretGeneratorType, config *crypto.GeneratorConfig) preparation.Validation { +func prepareAddSecretGeneratorConfig(a *instance.Aggregate, typ domain.SecretGeneratorType, config *crypto.GeneratorConfig) preparation.Validation { return func() (preparation.CreateCommands, error) { if !typ.Valid() { return nil, errors.ThrowInvalidArgument(nil, "V2-FGqVj", "Errors.InvalidArgument")