mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-12 03:57:32 +00:00
feat: New event user (#1156)
* feat: change user command side * feat: change user command side * feat: use states on write model * feat: command and query side in auth api * feat: auth commands * feat: check external idp id * feat: user state check * fix: error messages * fix: is active state
This commit is contained in:
@@ -63,12 +63,12 @@ func (r *CommandSide) ChangeDefaultIDPConfig(ctx context.Context, config *domain
|
||||
return nil, err
|
||||
}
|
||||
if existingIDP.State == domain.IDPConfigStateRemoved || existingIDP.State == domain.IDPConfigStateUnspecified {
|
||||
return nil, caos_errs.ThrowAlreadyExists(nil, "IAM-4M9so", "Errors.IAM.IDPConfig.NotExisting")
|
||||
return nil, caos_errs.ThrowNotFound(nil, "IAM-4M9so", "Errors.IAM.IDPConfig.NotExisting")
|
||||
}
|
||||
|
||||
changedEvent, hasChanged := existingIDP.NewChangedEvent(ctx, config.IDPConfigID, config.Name, config.StylingType)
|
||||
if !hasChanged {
|
||||
return nil, caos_errs.ThrowAlreadyExists(nil, "IAM-4M9vs", "Errors.IAM.LabelPolicy.NotChanged")
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "IAM-4M9vs", "Errors.IAM.LabelPolicy.NotChanged")
|
||||
}
|
||||
iamAgg := IAMAggregateFromWriteModel(&existingIDP.WriteModel)
|
||||
iamAgg.PushEvents(changedEvent)
|
||||
@@ -86,7 +86,7 @@ func (r *CommandSide) DeactivateDefaultIDPConfig(ctx context.Context, idpID stri
|
||||
return nil, err
|
||||
}
|
||||
if existingIDP.State != domain.IDPConfigStateActive {
|
||||
return nil, caos_errs.ThrowAlreadyExists(nil, "IAM-4M9so", "Errors.IAM.IDPConfig.NotActive")
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "IAM-4M9so", "Errors.IAM.IDPConfig.NotActive")
|
||||
}
|
||||
iamAgg := IAMAggregateFromWriteModel(&existingIDP.WriteModel)
|
||||
iamAgg.PushEvents(iam_repo.NewIDPConfigDeactivatedEvent(ctx, idpID))
|
||||
@@ -104,7 +104,7 @@ func (r *CommandSide) ReactivateDefaultIDPConfig(ctx context.Context, idpID stri
|
||||
return nil, err
|
||||
}
|
||||
if existingIDP.State != domain.IDPConfigStateInactive {
|
||||
return nil, caos_errs.ThrowAlreadyExists(nil, "IAM-5Mo0d", "Errors.IAM.IDPConfig.NotInactive")
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "IAM-5Mo0d", "Errors.IAM.IDPConfig.NotInactive")
|
||||
}
|
||||
iamAgg := IAMAggregateFromWriteModel(&existingIDP.WriteModel)
|
||||
iamAgg.PushEvents(iam_repo.NewIDPConfigReactivatedEvent(ctx, idpID))
|
||||
|
@@ -30,7 +30,7 @@ func (r *CommandSide) ChangeDefaultIDPOIDCConfig(ctx context.Context, config *do
|
||||
return nil, err
|
||||
}
|
||||
if !hasChanged {
|
||||
return nil, caos_errs.ThrowAlreadyExists(nil, "IAM-4M9vs", "Errors.IAM.LabelPolicy.NotChanged")
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "IAM-4M9vs", "Errors.IAM.LabelPolicy.NotChanged")
|
||||
}
|
||||
|
||||
iamAgg := IAMAggregateFromWriteModel(&existingConfig.WriteModel)
|
||||
|
@@ -23,7 +23,7 @@ func (r *CommandSide) AddIAMMember(ctx context.Context, member *domain.IAMMember
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if addedMember.IsActive {
|
||||
if addedMember.State == domain.MemberStateActive {
|
||||
return nil, errors.ThrowAlreadyExists(nil, "IAM-PtXi1", "Errors.IAM.Member.AlreadyExists")
|
||||
}
|
||||
|
||||
@@ -95,7 +95,7 @@ func (r *CommandSide) iamMemberWriteModelByID(ctx context.Context, iamID, userID
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !writeModel.IsActive {
|
||||
if writeModel.State == domain.MemberStateUnspecified || writeModel.State == domain.MemberStateRemoved {
|
||||
return nil, errors.ThrowNotFound(nil, "IAM-D8JxR", "Errors.NotFound")
|
||||
}
|
||||
|
||||
|
@@ -30,7 +30,7 @@ func (r *CommandSide) addDefaultLabelPolicy(ctx context.Context, iamAgg *iam_rep
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if addedPolicy.IsActive {
|
||||
if addedPolicy.State == domain.PolicyStateActive {
|
||||
return caos_errs.ThrowAlreadyExists(nil, "IAM-2B0ps", "Errors.IAM.LabelPolicy.AlreadyExists")
|
||||
}
|
||||
|
||||
@@ -46,13 +46,13 @@ func (r *CommandSide) ChangeDefaultLabelPolicy(ctx context.Context, policy *doma
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if !existingPolicy.IsActive {
|
||||
return nil, caos_errs.ThrowAlreadyExists(nil, "IAM-0K9dq", "Errors.IAM.LabelPolicy.NotFound")
|
||||
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
|
||||
return nil, caos_errs.ThrowNotFound(nil, "IAM-0K9dq", "Errors.IAM.LabelPolicy.NotFound")
|
||||
}
|
||||
|
||||
changedEvent, hasChanged := existingPolicy.NewChangedEvent(ctx, policy.PrimaryColor, policy.SecondaryColor)
|
||||
if !hasChanged {
|
||||
return nil, caos_errs.ThrowAlreadyExists(nil, "IAM-4M9vs", "Errors.IAM.LabelPolicy.NotChanged")
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "IAM-4M9vs", "Errors.IAM.LabelPolicy.NotChanged")
|
||||
}
|
||||
|
||||
iamAgg := IAMAggregateFromWriteModel(&existingPolicy.LabelPolicyWriteModel.WriteModel)
|
||||
|
@@ -42,7 +42,7 @@ func (r *CommandSide) addDefaultLoginPolicy(ctx context.Context, iamAgg *iam_rep
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if addedPolicy.IsActive {
|
||||
if addedPolicy.State == domain.PolicyStateActive {
|
||||
return caos_errs.ThrowAlreadyExists(nil, "IAM-2B0ps", "Errors.IAM.LoginPolicy.AlreadyExists")
|
||||
}
|
||||
|
||||
@@ -73,12 +73,12 @@ func (r *CommandSide) changeDefaultLoginPolicy(ctx context.Context, iamAgg *iam_
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !existingPolicy.IsActive {
|
||||
return caos_errs.ThrowAlreadyExists(nil, "IAM-M0sif", "Errors.IAM.LoginPolicy.NotFound")
|
||||
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
|
||||
return caos_errs.ThrowNotFound(nil, "IAM-M0sif", "Errors.IAM.LoginPolicy.NotFound")
|
||||
}
|
||||
changedEvent, hasChanged := existingPolicy.NewChangedEvent(ctx, policy.AllowUsernamePassword, policy.AllowRegister, policy.AllowExternalIdp, policy.ForceMFA, domain.PasswordlessType(policy.PasswordlessType))
|
||||
if !hasChanged {
|
||||
return caos_errs.ThrowAlreadyExists(nil, "IAM-5M9vdd", "Errors.IAM.LoginPolicy.NotChanged")
|
||||
return caos_errs.ThrowPreconditionFailed(nil, "IAM-5M9vdd", "Errors.IAM.LoginPolicy.NotChanged")
|
||||
}
|
||||
iamAgg.PushEvents(changedEvent)
|
||||
|
||||
@@ -92,7 +92,7 @@ func (r *CommandSide) AddIDPProviderToDefaultLoginPolicy(ctx context.Context, id
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if idpModel.IsActive {
|
||||
if idpModel.State == domain.IdentityProviderStateActive {
|
||||
return nil, caos_errs.ThrowAlreadyExists(nil, "IAM-2B0ps", "Errors.IAM.LoginPolicy.IDP.AlreadyExists")
|
||||
}
|
||||
|
||||
@@ -113,8 +113,8 @@ func (r *CommandSide) RemoveIDPProviderFromDefaultLoginPolicy(ctx context.Contex
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !idpModel.IsActive {
|
||||
return caos_errs.ThrowAlreadyExists(nil, "IAM-39fjs", "Errors.IAM.LoginPolicy.IDP.NotExisting")
|
||||
if idpModel.State == domain.IdentityProviderStateUnspecified || idpModel.State == domain.IdentityProviderStateRemoved {
|
||||
return caos_errs.ThrowNotFound(nil, "IAM-39fjs", "Errors.IAM.LoginPolicy.IDP.NotExisting")
|
||||
}
|
||||
iamAgg := IAMAggregateFromWriteModel(&idpModel.IdentityProviderWriteModel.WriteModel)
|
||||
iamAgg.PushEvents(iam_repo.NewIdentityProviderRemovedEvent(ctx, idpProvider.IDPConfigID))
|
||||
@@ -143,7 +143,7 @@ func (r *CommandSide) addSecondFactorToDefaultLoginPolicy(ctx context.Context, i
|
||||
return err
|
||||
}
|
||||
|
||||
if secondFactorModel.IsActive {
|
||||
if secondFactorModel.State == domain.FactorStateActive {
|
||||
return caos_errs.ThrowAlreadyExists(nil, "IAM-2B0ps", "Errors.IAM.LoginPolicy.MFA.AlreadyExists")
|
||||
}
|
||||
|
||||
@@ -158,8 +158,8 @@ func (r *CommandSide) RemoveSecondFactorFromDefaultLoginPolicy(ctx context.Conte
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if !secondFactorModel.IsActive {
|
||||
return caos_errs.ThrowAlreadyExists(nil, "IAM-3M9od", "Errors.IAM.LoginPolicy.MFA.NotExisting")
|
||||
if secondFactorModel.State == domain.FactorStateUnspecified || secondFactorModel.State == domain.FactorStateRemoved {
|
||||
return caos_errs.ThrowNotFound(nil, "IAM-3M9od", "Errors.IAM.LoginPolicy.MFA.NotExisting")
|
||||
}
|
||||
iamAgg := IAMAggregateFromWriteModel(&secondFactorModel.SecondFactorWriteModel.WriteModel)
|
||||
iamAgg.PushEvents(iam_repo.NewLoginPolicySecondFactorRemovedEvent(ctx, domain.SecondFactorType(secondFactor)))
|
||||
@@ -187,7 +187,7 @@ func (r *CommandSide) addMultiFactorToDefaultLoginPolicy(ctx context.Context, ia
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if multiFactorModel.IsActive {
|
||||
if multiFactorModel.State == domain.FactorStateActive {
|
||||
return caos_errs.ThrowAlreadyExists(nil, "IAM-3M9od", "Errors.IAM.LoginPolicy.MFA.AlreadyExists")
|
||||
}
|
||||
|
||||
@@ -202,8 +202,8 @@ func (r *CommandSide) RemoveMultiFactorFromDefaultLoginPolicy(ctx context.Contex
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if multiFactorModel.IsActive {
|
||||
return caos_errs.ThrowAlreadyExists(nil, "IAM-3M9df", "Errors.IAM.LoginPolicy.MFA.NotExisting")
|
||||
if multiFactorModel.State == domain.FactorStateUnspecified || multiFactorModel.State == domain.FactorStateRemoved {
|
||||
return caos_errs.ThrowNotFound(nil, "IAM-3M9df", "Errors.IAM.LoginPolicy.MFA.NotExisting")
|
||||
}
|
||||
iamAgg := IAMAggregateFromWriteModel(&multiFactorModel.MultiFactoryWriteModel.WriteModel)
|
||||
iamAgg.PushEvents(iam_repo.NewLoginPolicyMultiFactorRemovedEvent(ctx, domain.MultiFactorType(multiFactor)))
|
||||
|
@@ -41,7 +41,7 @@ func (r *CommandSide) addDefaultOrgIAMPolicy(ctx context.Context, iamAgg *iam_re
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if addedPolicy.IsActive {
|
||||
if addedPolicy.State == domain.PolicyStateActive {
|
||||
return caos_errs.ThrowAlreadyExists(nil, "IAM-Lk0dS", "Errors.IAM.OrgIAMPolicy.AlreadyExists")
|
||||
}
|
||||
iamAgg.PushEvents(iam_repo.NewOrgIAMPolicyAddedEvent(ctx, policy.UserLoginMustBeDomain))
|
||||
@@ -55,13 +55,13 @@ func (r *CommandSide) ChangeDefaultOrgIAMPolicy(ctx context.Context, policy *dom
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !existingPolicy.IsActive {
|
||||
return nil, caos_errs.ThrowAlreadyExists(nil, "IAM-0Pl0d", "Errors.IAM.OrgIAMPolicy.NotFound")
|
||||
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
|
||||
return nil, caos_errs.ThrowNotFound(nil, "IAM-0Pl0d", "Errors.IAM.OrgIAMPolicy.NotFound")
|
||||
}
|
||||
|
||||
changedEvent, hasChanged := existingPolicy.NewChangedEvent(ctx, policy.UserLoginMustBeDomain)
|
||||
if !hasChanged {
|
||||
return nil, caos_errs.ThrowAlreadyExists(nil, "IAM-4M9vs", "Errors.IAM.LabelPolicy.NotChanged")
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "IAM-4M9vs", "Errors.IAM.LabelPolicy.NotChanged")
|
||||
}
|
||||
|
||||
iamAgg := IAMAggregateFromWriteModel(&existingPolicy.PolicyOrgIAMWriteModel.WriteModel)
|
||||
|
@@ -30,7 +30,7 @@ func (r *CommandSide) addDefaultPasswordAgePolicy(ctx context.Context, iamAgg *i
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if addedPolicy.IsActive {
|
||||
if addedPolicy.State == domain.PolicyStateActive {
|
||||
return caos_errs.ThrowAlreadyExists(nil, "IAM-Lk0dS", "Errors.IAM.PasswordAgePolicy.AlreadyExists")
|
||||
}
|
||||
|
||||
@@ -45,13 +45,13 @@ func (r *CommandSide) ChangeDefaultPasswordAgePolicy(ctx context.Context, policy
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !existingPolicy.IsActive {
|
||||
return nil, caos_errs.ThrowAlreadyExists(nil, "IAM-0oPew", "Errors.IAM.PasswordAgePolicy.NotFound")
|
||||
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
|
||||
return nil, caos_errs.ThrowNotFound(nil, "IAM-0oPew", "Errors.IAM.PasswordAgePolicy.NotFound")
|
||||
}
|
||||
|
||||
changedEvent, hasChanged := existingPolicy.NewChangedEvent(ctx, policy.ExpireWarnDays, policy.MaxAgeDays)
|
||||
if !hasChanged {
|
||||
return nil, caos_errs.ThrowAlreadyExists(nil, "IAM-4M9vs", "Errors.IAM.LabelPolicy.NotChanged")
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "IAM-4M9vs", "Errors.IAM.LabelPolicy.NotChanged")
|
||||
}
|
||||
|
||||
iamAgg := IAMAggregateFromWriteModel(&existingPolicy.PasswordAgePolicyWriteModel.WriteModel)
|
||||
|
@@ -45,7 +45,7 @@ func (r *CommandSide) addDefaultPasswordComplexityPolicy(ctx context.Context, ia
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if addedPolicy.IsActive {
|
||||
if addedPolicy.State == domain.PolicyStateActive {
|
||||
return caos_errs.ThrowAlreadyExists(nil, "IAM-Lk0dS", "Errors.IAM.PasswordComplexityPolicy.AlreadyExists")
|
||||
}
|
||||
|
||||
@@ -64,13 +64,13 @@ func (r *CommandSide) ChangeDefaultPasswordComplexityPolicy(ctx context.Context,
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !existingPolicy.IsActive {
|
||||
return nil, caos_errs.ThrowAlreadyExists(nil, "IAM-0oPew", "Errors.IAM.PasswordAgePolicy.NotFound")
|
||||
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
|
||||
return nil, caos_errs.ThrowNotFound(nil, "IAM-0oPew", "Errors.IAM.PasswordAgePolicy.NotFound")
|
||||
}
|
||||
|
||||
changedEvent, hasChanged := existingPolicy.NewChangedEvent(ctx, policy.MinLength, policy.HasLowercase, policy.HasUppercase, policy.HasNumber, policy.HasSymbol)
|
||||
if !hasChanged {
|
||||
return nil, caos_errs.ThrowAlreadyExists(nil, "IAM-4M9vs", "Errors.IAM.LabelPolicy.NotChanged")
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "IAM-4M9vs", "Errors.IAM.LabelPolicy.NotChanged")
|
||||
}
|
||||
iamAgg := IAMAggregateFromWriteModel(&existingPolicy.PasswordComplexityPolicyWriteModel.WriteModel)
|
||||
iamAgg.PushEvents(changedEvent)
|
||||
|
@@ -30,7 +30,7 @@ func (r *CommandSide) addDefaultPasswordLockoutPolicy(ctx context.Context, iamAg
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if addedPolicy.IsActive {
|
||||
if addedPolicy.State == domain.PolicyStateActive {
|
||||
return caos_errs.ThrowAlreadyExists(nil, "IAM-0olDf", "Errors.IAM.PasswordLockoutPolicy.AlreadyExists")
|
||||
}
|
||||
|
||||
@@ -45,13 +45,13 @@ func (r *CommandSide) ChangeDefaultPasswordLockoutPolicy(ctx context.Context, po
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !existingPolicy.IsActive {
|
||||
return nil, caos_errs.ThrowAlreadyExists(nil, "IAM-0oPew", "Errors.IAM.PasswordLockoutPolicy.NotFound")
|
||||
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
|
||||
return nil, caos_errs.ThrowNotFound(nil, "IAM-0oPew", "Errors.IAM.PasswordLockoutPolicy.NotFound")
|
||||
}
|
||||
|
||||
changedEvent, hasChanged := existingPolicy.NewChangedEvent(ctx, policy.MaxAttempts, policy.ShowLockOutFailures)
|
||||
if !hasChanged {
|
||||
return nil, caos_errs.ThrowAlreadyExists(nil, "IAM-4M9vs", "Errors.IAM.PasswordLockoutPolicy.NotChanged")
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "IAM-4M9vs", "Errors.IAM.PasswordLockoutPolicy.NotChanged")
|
||||
}
|
||||
|
||||
iamAgg := IAMAggregateFromWriteModel(&existingPolicy.PasswordLockoutPolicyWriteModel.WriteModel)
|
||||
|
@@ -11,7 +11,7 @@ type IdentityProviderWriteModel struct {
|
||||
|
||||
IDPConfigID string
|
||||
IDPProviderType domain.IdentityProviderType
|
||||
IsActive bool
|
||||
State domain.IdentityProviderState
|
||||
}
|
||||
|
||||
func (wm *IdentityProviderWriteModel) Reduce() error {
|
||||
@@ -20,9 +20,9 @@ func (wm *IdentityProviderWriteModel) Reduce() error {
|
||||
case *policy.IdentityProviderAddedEvent:
|
||||
wm.IDPConfigID = e.IDPConfigID
|
||||
wm.IDPProviderType = e.IDPProviderType
|
||||
wm.IsActive = true
|
||||
wm.State = domain.IdentityProviderStateActive
|
||||
case *policy.IdentityProviderRemovedEvent:
|
||||
wm.IsActive = false
|
||||
wm.State = domain.IdentityProviderStateRemoved
|
||||
}
|
||||
}
|
||||
return wm.WriteModel.Reduce()
|
||||
|
@@ -2,15 +2,17 @@ package command
|
||||
|
||||
import (
|
||||
"github.com/caos/zitadel/internal/eventstore/v2"
|
||||
"github.com/caos/zitadel/internal/v2/domain"
|
||||
"github.com/caos/zitadel/internal/v2/repository/member"
|
||||
)
|
||||
|
||||
type MemberWriteModel struct {
|
||||
eventstore.WriteModel
|
||||
|
||||
UserID string
|
||||
Roles []string
|
||||
IsActive bool
|
||||
UserID string
|
||||
Roles []string
|
||||
|
||||
State domain.MemberState
|
||||
}
|
||||
|
||||
func NewMemberWriteModel(userID string) *MemberWriteModel {
|
||||
@@ -25,12 +27,12 @@ func (wm *MemberWriteModel) Reduce() error {
|
||||
case *member.MemberAddedEvent:
|
||||
wm.UserID = e.UserID
|
||||
wm.Roles = e.Roles
|
||||
wm.IsActive = true
|
||||
wm.State = domain.MemberStateActive
|
||||
case *member.MemberChangedEvent:
|
||||
wm.Roles = e.Roles
|
||||
case *member.MemberRemovedEvent:
|
||||
wm.Roles = nil
|
||||
wm.IsActive = false
|
||||
wm.State = domain.MemberStateRemoved
|
||||
}
|
||||
}
|
||||
return wm.WriteModel.Reduce()
|
||||
|
@@ -14,7 +14,7 @@ func (r *CommandSide) GetOrgIAMPolicy(ctx context.Context, orgID string) (*domai
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if policy.IsActive {
|
||||
if policy.State == domain.PolicyStateActive {
|
||||
return orgWriteModelToOrgIAMPolicy(policy), nil
|
||||
}
|
||||
return r.GetDefaultOrgIAMPolicy(ctx)
|
||||
@@ -26,7 +26,7 @@ func (r *CommandSide) AddOrgIAMPolicy(ctx context.Context, policy *domain.OrgIAM
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if addedPolicy.IsActive {
|
||||
if addedPolicy.State == domain.PolicyStateActive {
|
||||
return nil, caos_errs.ThrowAlreadyExists(nil, "ORG-5M0ds", "Errors.Org.OrgIAMPolicy.AlreadyExists")
|
||||
}
|
||||
orgAgg := ORGAggregateFromWriteModel(&addedPolicy.PolicyOrgIAMWriteModel.WriteModel)
|
||||
@@ -45,13 +45,13 @@ func (r *CommandSide) ChangeOrgIAMPolicy(ctx context.Context, policy *domain.Org
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !existingPolicy.IsActive {
|
||||
return nil, caos_errs.ThrowAlreadyExists(nil, "ORG-2N9sd", "Errors.Org.OrgIAMPolicy.NotFound")
|
||||
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
|
||||
return nil, caos_errs.ThrowNotFound(nil, "ORG-2N9sd", "Errors.Org.OrgIAMPolicy.NotFound")
|
||||
}
|
||||
|
||||
changedEvent, hasChanged := existingPolicy.NewChangedEvent(ctx, policy.UserLoginMustBeDomain)
|
||||
if !hasChanged {
|
||||
return nil, caos_errs.ThrowAlreadyExists(nil, "ORG-3M9ds", "Errors.Org.LabelPolicy.NotChanged")
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "ORG-3M9ds", "Errors.Org.LabelPolicy.NotChanged")
|
||||
}
|
||||
|
||||
orgAgg := ORGAggregateFromWriteModel(&existingPolicy.PolicyOrgIAMWriteModel.WriteModel)
|
||||
|
@@ -11,7 +11,7 @@ func (r *CommandSide) GetOrgPasswordComplexityPolicy(ctx context.Context, orgID
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if policy.IsActive {
|
||||
if policy.State == domain.PolicyStateActive {
|
||||
return orgWriteModelToPasswordComplexityPolicy(policy), nil
|
||||
}
|
||||
return r.GetDefaultPasswordComplexityPolicy(ctx)
|
||||
|
@@ -2,6 +2,7 @@ package command
|
||||
|
||||
import (
|
||||
"github.com/caos/zitadel/internal/eventstore/v2"
|
||||
"github.com/caos/zitadel/internal/v2/domain"
|
||||
"github.com/caos/zitadel/internal/v2/repository/policy"
|
||||
)
|
||||
|
||||
@@ -10,7 +11,8 @@ type LabelPolicyWriteModel struct {
|
||||
|
||||
PrimaryColor string
|
||||
SecondaryColor string
|
||||
IsActive bool
|
||||
|
||||
State domain.PolicyState
|
||||
}
|
||||
|
||||
func (wm *LabelPolicyWriteModel) Reduce() error {
|
||||
@@ -19,7 +21,7 @@ func (wm *LabelPolicyWriteModel) Reduce() error {
|
||||
case *policy.LabelPolicyAddedEvent:
|
||||
wm.PrimaryColor = e.PrimaryColor
|
||||
wm.SecondaryColor = e.SecondaryColor
|
||||
wm.IsActive = true
|
||||
wm.State = domain.PolicyStateActive
|
||||
case *policy.LabelPolicyChangedEvent:
|
||||
if e.PrimaryColor != nil {
|
||||
wm.PrimaryColor = *e.PrimaryColor
|
||||
@@ -28,7 +30,7 @@ func (wm *LabelPolicyWriteModel) Reduce() error {
|
||||
wm.SecondaryColor = *e.SecondaryColor
|
||||
}
|
||||
case *policy.LabelPolicyRemovedEvent:
|
||||
wm.IsActive = false
|
||||
wm.State = domain.PolicyStateRemoved
|
||||
}
|
||||
}
|
||||
return wm.WriteModel.Reduce()
|
||||
|
@@ -8,8 +8,8 @@ import (
|
||||
|
||||
type SecondFactorWriteModel struct {
|
||||
eventstore.WriteModel
|
||||
MFAType domain.SecondFactorType
|
||||
IsActive bool
|
||||
MFAType domain.SecondFactorType
|
||||
State domain.FactorState
|
||||
}
|
||||
|
||||
func (wm *SecondFactorWriteModel) Reduce() error {
|
||||
@@ -17,10 +17,10 @@ func (wm *SecondFactorWriteModel) Reduce() error {
|
||||
switch e := event.(type) {
|
||||
case *policy.SecondFactorAddedEvent:
|
||||
wm.MFAType = e.MFAType
|
||||
wm.IsActive = true
|
||||
wm.State = domain.FactorStateActive
|
||||
case *policy.SecondFactorRemovedEvent:
|
||||
wm.MFAType = e.MFAType
|
||||
wm.IsActive = false
|
||||
wm.State = domain.FactorStateRemoved
|
||||
}
|
||||
}
|
||||
return wm.WriteModel.Reduce()
|
||||
@@ -28,8 +28,8 @@ func (wm *SecondFactorWriteModel) Reduce() error {
|
||||
|
||||
type MultiFactoryWriteModel struct {
|
||||
eventstore.WriteModel
|
||||
MFAType domain.MultiFactorType
|
||||
IsActive bool
|
||||
MFAType domain.MultiFactorType
|
||||
State domain.FactorState
|
||||
}
|
||||
|
||||
func (wm *MultiFactoryWriteModel) Reduce() error {
|
||||
@@ -37,10 +37,10 @@ func (wm *MultiFactoryWriteModel) Reduce() error {
|
||||
switch e := event.(type) {
|
||||
case *policy.MultiFactorAddedEvent:
|
||||
wm.MFAType = e.MFAType
|
||||
wm.IsActive = true
|
||||
wm.State = domain.FactorStateActive
|
||||
case *policy.MultiFactorRemovedEvent:
|
||||
wm.MFAType = e.MFAType
|
||||
wm.IsActive = false
|
||||
wm.State = domain.FactorStateRemoved
|
||||
}
|
||||
}
|
||||
return wm.WriteModel.Reduce()
|
||||
|
@@ -14,7 +14,7 @@ type LoginPolicyWriteModel struct {
|
||||
AllowExternalIDP bool
|
||||
ForceMFA bool
|
||||
PasswordlessType domain.PasswordlessType
|
||||
IsActive bool
|
||||
State domain.PolicyState
|
||||
}
|
||||
|
||||
func (wm *LoginPolicyWriteModel) Reduce() error {
|
||||
@@ -26,7 +26,7 @@ func (wm *LoginPolicyWriteModel) Reduce() error {
|
||||
wm.AllowExternalIDP = e.AllowExternalIDP
|
||||
wm.ForceMFA = e.ForceMFA
|
||||
wm.PasswordlessType = e.PasswordlessType
|
||||
wm.IsActive = true
|
||||
wm.State = domain.PolicyStateActive
|
||||
case *policy.LoginPolicyChangedEvent:
|
||||
if e.AllowRegister != nil {
|
||||
wm.AllowRegister = *e.AllowRegister
|
||||
@@ -44,7 +44,7 @@ func (wm *LoginPolicyWriteModel) Reduce() error {
|
||||
wm.PasswordlessType = *e.PasswordlessType
|
||||
}
|
||||
case *policy.LoginPolicyRemovedEvent:
|
||||
wm.IsActive = false
|
||||
wm.State = domain.PolicyStateRemoved
|
||||
}
|
||||
}
|
||||
return wm.WriteModel.Reduce()
|
||||
|
@@ -2,6 +2,7 @@ package command
|
||||
|
||||
import (
|
||||
"github.com/caos/zitadel/internal/eventstore/v2"
|
||||
"github.com/caos/zitadel/internal/v2/domain"
|
||||
"github.com/caos/zitadel/internal/v2/repository/policy"
|
||||
)
|
||||
|
||||
@@ -9,7 +10,7 @@ type PolicyOrgIAMWriteModel struct {
|
||||
eventstore.WriteModel
|
||||
|
||||
UserLoginMustBeDomain bool
|
||||
IsActive bool
|
||||
State domain.PolicyState
|
||||
}
|
||||
|
||||
func (wm *PolicyOrgIAMWriteModel) Reduce() error {
|
||||
@@ -17,7 +18,7 @@ func (wm *PolicyOrgIAMWriteModel) Reduce() error {
|
||||
switch e := event.(type) {
|
||||
case *policy.OrgIAMPolicyAddedEvent:
|
||||
wm.UserLoginMustBeDomain = e.UserLoginMustBeDomain
|
||||
wm.IsActive = true
|
||||
wm.State = domain.PolicyStateActive
|
||||
case *policy.OrgIAMPolicyChangedEvent:
|
||||
if e.UserLoginMustBeDomain != nil {
|
||||
wm.UserLoginMustBeDomain = *e.UserLoginMustBeDomain
|
||||
|
@@ -2,6 +2,7 @@ package command
|
||||
|
||||
import (
|
||||
"github.com/caos/zitadel/internal/eventstore/v2"
|
||||
"github.com/caos/zitadel/internal/v2/domain"
|
||||
"github.com/caos/zitadel/internal/v2/repository/policy"
|
||||
)
|
||||
|
||||
@@ -10,7 +11,7 @@ type PasswordAgePolicyWriteModel struct {
|
||||
|
||||
ExpireWarnDays uint64
|
||||
MaxAgeDays uint64
|
||||
IsActive bool
|
||||
State domain.PolicyState
|
||||
}
|
||||
|
||||
func (wm *PasswordAgePolicyWriteModel) Reduce() error {
|
||||
@@ -19,7 +20,7 @@ func (wm *PasswordAgePolicyWriteModel) Reduce() error {
|
||||
case *policy.PasswordAgePolicyAddedEvent:
|
||||
wm.ExpireWarnDays = e.ExpireWarnDays
|
||||
wm.MaxAgeDays = e.MaxAgeDays
|
||||
wm.IsActive = true
|
||||
wm.State = domain.PolicyStateActive
|
||||
case *policy.PasswordAgePolicyChangedEvent:
|
||||
if e.ExpireWarnDays != nil {
|
||||
wm.ExpireWarnDays = *e.ExpireWarnDays
|
||||
@@ -28,7 +29,7 @@ func (wm *PasswordAgePolicyWriteModel) Reduce() error {
|
||||
wm.ExpireWarnDays = *e.ExpireWarnDays
|
||||
}
|
||||
case *policy.PasswordAgePolicyRemovedEvent:
|
||||
wm.IsActive = false
|
||||
wm.State = domain.PolicyStateRemoved
|
||||
}
|
||||
}
|
||||
return wm.WriteModel.Reduce()
|
||||
|
@@ -2,6 +2,7 @@ package command
|
||||
|
||||
import (
|
||||
"github.com/caos/zitadel/internal/eventstore/v2"
|
||||
"github.com/caos/zitadel/internal/v2/domain"
|
||||
"github.com/caos/zitadel/internal/v2/repository/policy"
|
||||
)
|
||||
|
||||
@@ -13,7 +14,7 @@ type PasswordComplexityPolicyWriteModel struct {
|
||||
HasUpperCase bool
|
||||
HasNumber bool
|
||||
HasSymbol bool
|
||||
IsActive bool
|
||||
State domain.PolicyState
|
||||
}
|
||||
|
||||
func (wm *PasswordComplexityPolicyWriteModel) Reduce() error {
|
||||
@@ -25,7 +26,7 @@ func (wm *PasswordComplexityPolicyWriteModel) Reduce() error {
|
||||
wm.HasUpperCase = e.HasUpperCase
|
||||
wm.HasNumber = e.HasNumber
|
||||
wm.HasSymbol = e.HasSymbol
|
||||
wm.IsActive = true
|
||||
wm.State = domain.PolicyStateActive
|
||||
case *policy.PasswordComplexityPolicyChangedEvent:
|
||||
if e.MinLength != nil {
|
||||
wm.MinLength = *e.MinLength
|
||||
@@ -43,7 +44,7 @@ func (wm *PasswordComplexityPolicyWriteModel) Reduce() error {
|
||||
wm.HasSymbol = *e.HasSymbol
|
||||
}
|
||||
case *policy.PasswordComplexityPolicyRemovedEvent:
|
||||
wm.IsActive = false
|
||||
wm.State = domain.PolicyStateRemoved
|
||||
}
|
||||
}
|
||||
return wm.WriteModel.Reduce()
|
||||
|
@@ -2,6 +2,7 @@ package command
|
||||
|
||||
import (
|
||||
"github.com/caos/zitadel/internal/eventstore/v2"
|
||||
"github.com/caos/zitadel/internal/v2/domain"
|
||||
"github.com/caos/zitadel/internal/v2/repository/policy"
|
||||
)
|
||||
|
||||
@@ -10,7 +11,7 @@ type PasswordLockoutPolicyWriteModel struct {
|
||||
|
||||
MaxAttempts uint64
|
||||
ShowLockOutFailures bool
|
||||
IsActive bool
|
||||
State domain.PolicyState
|
||||
}
|
||||
|
||||
func (wm *PasswordLockoutPolicyWriteModel) Reduce() error {
|
||||
@@ -19,7 +20,7 @@ func (wm *PasswordLockoutPolicyWriteModel) Reduce() error {
|
||||
case *policy.PasswordLockoutPolicyAddedEvent:
|
||||
wm.MaxAttempts = e.MaxAttempts
|
||||
wm.ShowLockOutFailures = e.ShowLockOutFailures
|
||||
wm.IsActive = true
|
||||
wm.State = domain.PolicyStateActive
|
||||
case *policy.PasswordLockoutPolicyChangedEvent:
|
||||
if e.MaxAttempts != nil {
|
||||
wm.MaxAttempts = *e.MaxAttempts
|
||||
@@ -28,7 +29,7 @@ func (wm *PasswordLockoutPolicyWriteModel) Reduce() error {
|
||||
wm.ShowLockOutFailures = *e.ShowLockOutFailures
|
||||
}
|
||||
case *policy.PasswordLockoutPolicyRemovedEvent:
|
||||
wm.IsActive = false
|
||||
wm.State = domain.PolicyStateRemoved
|
||||
}
|
||||
}
|
||||
return wm.WriteModel.Reduce()
|
||||
|
@@ -8,19 +8,19 @@ import (
|
||||
"github.com/caos/zitadel/internal/v2/repository/user"
|
||||
)
|
||||
|
||||
func (r *CommandSide) AddUser(ctx context.Context, user *domain.User) (*domain.User, error) {
|
||||
func (r *CommandSide) AddUser(ctx context.Context, orgID string, user *domain.User) (*domain.User, error) {
|
||||
if !user.IsValid() {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2N9fs", "Errors.User.Invalid")
|
||||
}
|
||||
|
||||
if user.Human != nil {
|
||||
human, err := r.AddHuman(ctx, user.ResourceOwner, user.UserName, user.Human)
|
||||
human, err := r.AddHuman(ctx, orgID, user.UserName, user.Human)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &domain.User{UserName: user.UserName, Human: human}, nil
|
||||
} else if user.Machine != nil {
|
||||
machine, err := r.AddMachine(ctx, user.ResourceOwner, user.UserName, user.Machine)
|
||||
machine, err := r.AddMachine(ctx, orgID, user.UserName, user.Machine)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -29,13 +29,13 @@ func (r *CommandSide) AddUser(ctx context.Context, user *domain.User) (*domain.U
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-8K0df", "Errors.User.TypeUndefined")
|
||||
}
|
||||
|
||||
func (r *CommandSide) RegisterUser(ctx context.Context, user *domain.User) (*domain.User, error) {
|
||||
func (r *CommandSide) RegisterUser(ctx context.Context, orgID string, user *domain.User) (*domain.User, error) {
|
||||
if !user.IsValid() {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2N9fs", "Errors.User.Invalid")
|
||||
}
|
||||
|
||||
if user.Human != nil {
|
||||
human, err := r.RegisterHuman(ctx, user.ResourceOwner, user.UserName, user.Human, nil)
|
||||
human, err := r.RegisterHuman(ctx, orgID, user.UserName, user.Human, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -44,13 +44,46 @@ func (r *CommandSide) RegisterUser(ctx context.Context, user *domain.User) (*dom
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-8K0df", "Errors.User.TypeUndefined")
|
||||
}
|
||||
|
||||
func (r *CommandSide) ChangeUsername(ctx context.Context, orgID, userID, userName string) error {
|
||||
if orgID == "" || userID == "" || userName == "" {
|
||||
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2N9fs", "Errors.IDMissing")
|
||||
}
|
||||
existingUser, err := r.userWriteModelByID(ctx, userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if existingUser.UserState == domain.UserStateUnspecified || existingUser.UserState == domain.UserStateDeleted {
|
||||
return caos_errs.ThrowNotFound(nil, "COMMAND-5N9ds", "Errors.User.NotFound")
|
||||
}
|
||||
if existingUser.UserName == userName {
|
||||
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2M9fs", "Errors.User.UsernameNotChanged")
|
||||
}
|
||||
|
||||
orgIAMPolicy, err := r.GetOrgIAMPolicy(ctx, orgID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := CheckOrgIAMPolicyForUserName(userName, orgIAMPolicy); err != nil {
|
||||
return err
|
||||
}
|
||||
userAgg := UserAggregateFromWriteModel(&existingUser.WriteModel)
|
||||
userAgg.PushEvents(user.NewUsernameChangedEvent(ctx, userName))
|
||||
//TODO: Check Uniqueness
|
||||
//TODO: release old username, set new unique username
|
||||
|
||||
return r.eventstore.PushAggregate(ctx, existingUser, userAgg)
|
||||
}
|
||||
|
||||
func (r *CommandSide) DeactivateUser(ctx context.Context, userID string) (*domain.User, error) {
|
||||
if userID == "" {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-m0gDf", "Errors.User.UserIDMissing")
|
||||
}
|
||||
existingUser, err := r.userWriteModelByID(ctx, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if existingUser.UserState != domain.UserStateUnspecified || existingUser.UserState != domain.UserStateDeleted {
|
||||
return nil, caos_errs.ThrowAlreadyExists(nil, "COMMAND-3M9ds", "Errors.User.NotFound")
|
||||
if existingUser.UserState == domain.UserStateUnspecified || existingUser.UserState == domain.UserStateDeleted {
|
||||
return nil, caos_errs.ThrowNotFound(nil, "COMMAND-3M9ds", "Errors.User.NotFound")
|
||||
}
|
||||
if existingUser.UserState == domain.UserStateInactive {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-5M0sf", "Errors.User.AlreadyInactive")
|
||||
@@ -66,12 +99,15 @@ func (r *CommandSide) DeactivateUser(ctx context.Context, userID string) (*domai
|
||||
}
|
||||
|
||||
func (r *CommandSide) ReactivateUser(ctx context.Context, userID string) (*domain.User, error) {
|
||||
if userID == "" {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-4M9ds", "Errors.User.UserIDMissing")
|
||||
}
|
||||
existingUser, err := r.userWriteModelByID(ctx, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if existingUser.UserState != domain.UserStateUnspecified || existingUser.UserState != domain.UserStateDeleted {
|
||||
return nil, caos_errs.ThrowAlreadyExists(nil, "COMMAND-4M0sd", "Errors.User.NotFound")
|
||||
if existingUser.UserState == domain.UserStateUnspecified || existingUser.UserState == domain.UserStateDeleted {
|
||||
return nil, caos_errs.ThrowNotFound(nil, "COMMAND-4M0sd", "Errors.User.NotFound")
|
||||
}
|
||||
if existingUser.UserState != domain.UserStateInactive {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-6M0sf", "Errors.User.NotInactive")
|
||||
@@ -87,12 +123,15 @@ func (r *CommandSide) ReactivateUser(ctx context.Context, userID string) (*domai
|
||||
}
|
||||
|
||||
func (r *CommandSide) LockUser(ctx context.Context, userID string) (*domain.User, error) {
|
||||
if userID == "" {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2M0sd", "Errors.User.UserIDMissing")
|
||||
}
|
||||
existingUser, err := r.userWriteModelByID(ctx, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if existingUser.UserState != domain.UserStateUnspecified || existingUser.UserState != domain.UserStateDeleted {
|
||||
return nil, caos_errs.ThrowAlreadyExists(nil, "COMMAND-5M9fs", "Errors.User.NotFound")
|
||||
if existingUser.UserState == domain.UserStateUnspecified || existingUser.UserState == domain.UserStateDeleted {
|
||||
return nil, caos_errs.ThrowNotFound(nil, "COMMAND-5M9fs", "Errors.User.NotFound")
|
||||
}
|
||||
if existingUser.UserState != domain.UserStateActive && existingUser.UserState != domain.UserStateInitial {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2M9fs", "Errors.User.ShouldBeActiveOrInitial")
|
||||
@@ -108,12 +147,15 @@ func (r *CommandSide) LockUser(ctx context.Context, userID string) (*domain.User
|
||||
}
|
||||
|
||||
func (r *CommandSide) UnlockUser(ctx context.Context, userID string) (*domain.User, error) {
|
||||
if userID == "" {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-M0dse", "Errors.User.UserIDMissing")
|
||||
}
|
||||
existingUser, err := r.userWriteModelByID(ctx, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if existingUser.UserState != domain.UserStateUnspecified || existingUser.UserState != domain.UserStateDeleted {
|
||||
return nil, caos_errs.ThrowAlreadyExists(nil, "COMMAND-M0dos", "Errors.User.NotFound")
|
||||
if existingUser.UserState == domain.UserStateUnspecified || existingUser.UserState == domain.UserStateDeleted {
|
||||
return nil, caos_errs.ThrowNotFound(nil, "COMMAND-M0dos", "Errors.User.NotFound")
|
||||
}
|
||||
if existingUser.UserState != domain.UserStateLocked {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-4M0ds", "Errors.User.NotLocked")
|
||||
@@ -129,16 +171,20 @@ func (r *CommandSide) UnlockUser(ctx context.Context, userID string) (*domain.Us
|
||||
}
|
||||
|
||||
func (r *CommandSide) RemoveUser(ctx context.Context, userID string) error {
|
||||
if userID == "" {
|
||||
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2M0ds", "Errors.User.UserIDMissing")
|
||||
}
|
||||
existingUser, err := r.userWriteModelByID(ctx, userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if existingUser.UserState != domain.UserStateDeleted {
|
||||
return caos_errs.ThrowAlreadyExists(nil, "COMMAND-5M0od", "Errors.User.NotFound")
|
||||
if existingUser.UserState == domain.UserStateUnspecified || existingUser.UserState == domain.UserStateDeleted {
|
||||
return caos_errs.ThrowNotFound(nil, "COMMAND-5M0od", "Errors.User.NotFound")
|
||||
}
|
||||
userAgg := UserAggregateFromWriteModel(&existingUser.WriteModel)
|
||||
userAgg.PushEvents(user.NewUserRemovedEvent(ctx))
|
||||
//TODO: release unqie username
|
||||
//TODO: remove user grants
|
||||
|
||||
return r.eventstore.PushAggregate(ctx, existingUser, userAgg)
|
||||
}
|
||||
|
@@ -57,6 +57,13 @@ func writeModelToEmail(wm *HumanEmailWriteModel) *domain.Email {
|
||||
}
|
||||
}
|
||||
|
||||
func writeModelToPhone(wm *HumanPhoneWriteModel) *domain.Phone {
|
||||
return &domain.Phone{
|
||||
ObjectRoot: writeModelToObjectRoot(wm.WriteModel),
|
||||
PhoneNumber: wm.Phone,
|
||||
IsPhoneVerified: wm.IsPhoneVerified,
|
||||
}
|
||||
}
|
||||
func writeModelToAddress(wm *HumanAddressWriteModel) *domain.Address {
|
||||
return &domain.Address{
|
||||
ObjectRoot: writeModelToObjectRoot(wm.WriteModel),
|
||||
|
@@ -27,9 +27,13 @@ func (r *CommandSide) AddHuman(ctx context.Context, orgID, username string, huma
|
||||
|
||||
addedHuman := NewHumanWriteModel(human.AggregateID)
|
||||
//TODO: Check Unique Username
|
||||
human.CheckOrgIAMPolicy(username, orgIAMPolicy)
|
||||
if err := human.CheckOrgIAMPolicy(username, orgIAMPolicy); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
human.SetNamesAsDisplayname()
|
||||
human.HashPasswordIfExisting(pwPolicy, r.userPasswordAlg, true)
|
||||
if err := human.HashPasswordIfExisting(pwPolicy, r.userPasswordAlg, true); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
userAgg := UserAggregateFromWriteModel(&addedHuman.WriteModel)
|
||||
addEvent := user.NewHumanAddedEvent(
|
||||
@@ -107,9 +111,13 @@ func (r *CommandSide) RegisterHuman(ctx context.Context, orgID, username string,
|
||||
|
||||
addedHuman := NewHumanWriteModel(human.AggregateID)
|
||||
//TODO: Check Unique Username or unique external idp
|
||||
human.CheckOrgIAMPolicy(username, orgIAMPolicy)
|
||||
if err := human.CheckOrgIAMPolicy(username, orgIAMPolicy); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
human.SetNamesAsDisplayname()
|
||||
human.HashPasswordIfExisting(pwPolicy, r.userPasswordAlg, true)
|
||||
if err := human.HashPasswordIfExisting(pwPolicy, r.userPasswordAlg, true); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
userAgg := UserAggregateFromWriteModel(&addedHuman.WriteModel)
|
||||
addEvent := user.NewHumanRegisteredEvent(
|
||||
@@ -144,7 +152,7 @@ func (r *CommandSide) RegisterHuman(ctx context.Context, orgID, username string,
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
user.NewHumanInitialCodeAddedEvent(ctx, initCode.Code, initCode.Expiry)
|
||||
userAgg.PushEvents(user.NewHumanInitialCodeAddedEvent(ctx, initCode.Code, initCode.Expiry))
|
||||
}
|
||||
|
||||
if human.Email != nil && human.EmailAddress != "" && human.IsEmailVerified {
|
||||
@@ -155,7 +163,9 @@ func (r *CommandSide) RegisterHuman(ctx context.Context, orgID, username string,
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
user.NewHumanPhoneCodeAddedEvent(ctx, phoneCode.Code, phoneCode.Expiry)
|
||||
userAgg.PushEvents(user.NewHumanPhoneCodeAddedEvent(ctx, phoneCode.Code, phoneCode.Expiry))
|
||||
} else if human.Phone != nil && human.PhoneNumber != "" && human.IsPhoneVerified {
|
||||
userAgg.PushEvents(user.NewHumanPhoneVerifiedEvent(ctx))
|
||||
}
|
||||
|
||||
err = r.eventstore.PushAggregate(ctx, addedHuman, userAgg)
|
||||
@@ -165,3 +175,31 @@ func (r *CommandSide) RegisterHuman(ctx context.Context, orgID, username string,
|
||||
|
||||
return writeModelToHuman(addedHuman), nil
|
||||
}
|
||||
|
||||
func (r *CommandSide) ResendInitialMail(ctx context.Context, userID, email string) (err error) {
|
||||
if userID == "" {
|
||||
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2M9fs", "Errors.User.UserIDMissing")
|
||||
}
|
||||
|
||||
existingEmail, err := r.emailWriteModel(ctx, userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if existingEmail.UserState == domain.UserStateUnspecified || existingEmail.UserState == domain.UserStateDeleted {
|
||||
return caos_errs.ThrowNotFound(nil, "COMMAND-2M9df", "Errors.User.NotFound")
|
||||
}
|
||||
if existingEmail.UserState != domain.UserStateInitial {
|
||||
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2M9sd", "Errors.User.AlreadyInitialised")
|
||||
}
|
||||
userAgg := UserAggregateFromWriteModel(&existingEmail.WriteModel)
|
||||
if email != "" && existingEmail.Email != email {
|
||||
changedEvent, _ := existingEmail.NewChangedEvent(ctx, email)
|
||||
userAgg.PushEvents(changedEvent)
|
||||
}
|
||||
initCode, err := domain.NewInitUserCode(r.initializeUserCode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
userAgg.PushEvents(user.NewHumanInitialCodeAddedEvent(ctx, initCode.Code, initCode.Expiry))
|
||||
return r.eventstore.PushAggregate(ctx, existingEmail, userAgg)
|
||||
}
|
||||
|
@@ -12,12 +12,12 @@ func (r *CommandSide) ChangeHumanAddress(ctx context.Context, address *domain.Ad
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if existingAddress.UserState == domain.UserStateUnspecified || existingAddress.UserState == domain.UserStateDeleted {
|
||||
return nil, caos_errs.ThrowAlreadyExists(nil, "COMMAND-0pLdo", "Errors.User.Address.NotFound")
|
||||
if existingAddress.State == domain.AddressStateUnspecified || existingAddress.State == domain.AddressStateRemoved {
|
||||
return nil, caos_errs.ThrowNotFound(nil, "COMMAND-0pLdo", "Errors.User.Address.NotFound")
|
||||
}
|
||||
changedEvent, hasChanged := existingAddress.NewChangedEvent(ctx, address.Country, address.Locality, address.PostalCode, address.Region, address.StreetAddress)
|
||||
if !hasChanged {
|
||||
return nil, caos_errs.ThrowAlreadyExists(nil, "COMMAND-3M0cs", "Errors.User.Address.NotChanged")
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-3M0cs", "Errors.User.Address.NotChanged")
|
||||
}
|
||||
userAgg := UserAggregateFromWriteModel(&existingAddress.WriteModel)
|
||||
userAgg.PushEvents(changedEvent)
|
||||
|
@@ -16,7 +16,7 @@ type HumanAddressWriteModel struct {
|
||||
Region string
|
||||
StreetAddress string
|
||||
|
||||
UserState domain.UserState
|
||||
State domain.AddressState
|
||||
}
|
||||
|
||||
func NewHumanAddressWriteModel(userID string) *HumanAddressWriteModel {
|
||||
@@ -49,14 +49,14 @@ func (wm *HumanAddressWriteModel) Reduce() error {
|
||||
wm.PostalCode = e.PostalCode
|
||||
wm.Region = e.Region
|
||||
wm.StreetAddress = e.StreetAddress
|
||||
wm.UserState = domain.UserStateActive
|
||||
wm.State = domain.AddressStateActive
|
||||
case *user.HumanRegisteredEvent:
|
||||
wm.Country = e.Country
|
||||
wm.Locality = e.Locality
|
||||
wm.PostalCode = e.PostalCode
|
||||
wm.Region = e.Region
|
||||
wm.StreetAddress = e.StreetAddress
|
||||
wm.UserState = domain.UserStateActive
|
||||
wm.State = domain.AddressStateActive
|
||||
case *user.HumanAddressChangedEvent:
|
||||
if e.Country != nil {
|
||||
wm.Country = *e.Country
|
||||
@@ -74,7 +74,7 @@ func (wm *HumanAddressWriteModel) Reduce() error {
|
||||
wm.StreetAddress = *e.StreetAddress
|
||||
}
|
||||
case *user.UserRemovedEvent:
|
||||
wm.UserState = domain.UserStateDeleted
|
||||
wm.State = domain.AddressStateRemoved
|
||||
}
|
||||
}
|
||||
return wm.WriteModel.Reduce()
|
||||
|
@@ -5,10 +5,11 @@ import (
|
||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/telemetry/tracing"
|
||||
"github.com/caos/zitadel/internal/v2/domain"
|
||||
"github.com/caos/zitadel/internal/v2/repository/user"
|
||||
)
|
||||
|
||||
func (r *CommandSide) ChangeHumanEmail(ctx context.Context, email *domain.Email) (*domain.Email, error) {
|
||||
if !email.IsValid() {
|
||||
if !email.IsValid() || email.AggregateID == "" {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-4M9sf", "Errors.Email.Invalid")
|
||||
}
|
||||
|
||||
@@ -17,15 +18,25 @@ func (r *CommandSide) ChangeHumanEmail(ctx context.Context, email *domain.Email)
|
||||
return nil, err
|
||||
}
|
||||
if existingEmail.UserState == domain.UserStateUnspecified || existingEmail.UserState == domain.UserStateDeleted {
|
||||
return nil, caos_errs.ThrowAlreadyExists(nil, "COMMAND-0Pe4r", "Errors.User.Email.NotFound")
|
||||
return nil, caos_errs.ThrowNotFound(nil, "COMMAND-0Pe4r", "Errors.User.Email.NotFound")
|
||||
}
|
||||
changedEvent, hasChanged := existingEmail.NewChangedEvent(ctx, email.EmailAddress)
|
||||
if !hasChanged {
|
||||
return nil, caos_errs.ThrowAlreadyExists(nil, "COMMAND-2M9fs", "Errors.User.Email.NotChanged")
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2M9fs", "Errors.User.Email.NotChanged")
|
||||
}
|
||||
userAgg := UserAggregateFromWriteModel(&existingEmail.WriteModel)
|
||||
userAgg.PushEvents(changedEvent)
|
||||
|
||||
if email.IsEmailVerified {
|
||||
userAgg.PushEvents(user.NewHumanEmailVerifiedEvent(ctx))
|
||||
} else {
|
||||
emailCode, err := domain.NewEmailCode(r.emailVerificationCode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
userAgg.PushEvents(user.NewHumanEmailCodeAddedEvent(ctx, emailCode.Code, emailCode.Expiry))
|
||||
}
|
||||
|
||||
err = r.eventstore.PushAggregate(ctx, existingEmail, userAgg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -34,6 +45,34 @@ func (r *CommandSide) ChangeHumanEmail(ctx context.Context, email *domain.Email)
|
||||
return writeModelToEmail(existingEmail), nil
|
||||
}
|
||||
|
||||
func (r *CommandSide) CreateHumanEmailVerificationCode(ctx context.Context, userID string) error {
|
||||
if userID == "" {
|
||||
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-4M0ds", "Errors.User.UserIDMissing")
|
||||
}
|
||||
|
||||
existingEmail, err := r.emailWriteModel(ctx, userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if existingEmail.UserState == domain.UserStateUnspecified || existingEmail.UserState == domain.UserStateDeleted {
|
||||
return caos_errs.ThrowNotFound(nil, "COMMAND-0Pe4r", "Errors.User.Email.NotFound")
|
||||
}
|
||||
if existingEmail.UserState == domain.UserStateInitial {
|
||||
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-E3fbw", "Errors.User.NotInitialised")
|
||||
}
|
||||
if existingEmail.IsEmailVerified {
|
||||
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-3M9ds", "Errors.User.Email.AlreadyVerified")
|
||||
}
|
||||
userAgg := UserAggregateFromWriteModel(&existingEmail.WriteModel)
|
||||
emailCode, err := domain.NewEmailCode(r.emailVerificationCode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
userAgg.PushEvents(user.NewHumanEmailCodeAddedEvent(ctx, emailCode.Code, emailCode.Expiry))
|
||||
|
||||
return r.eventstore.PushAggregate(ctx, existingEmail, userAgg)
|
||||
}
|
||||
|
||||
func (r *CommandSide) emailWriteModel(ctx context.Context, userID string) (writeModel *HumanEmailWriteModel, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
@@ -44,15 +44,18 @@ func (wm *HumanEmailWriteModel) Reduce() error {
|
||||
switch e := event.(type) {
|
||||
case *user.HumanAddedEvent:
|
||||
wm.Email = e.EmailAddress
|
||||
wm.UserState = domain.UserStateActive
|
||||
wm.UserState = domain.UserStateInitial
|
||||
case *user.HumanRegisteredEvent:
|
||||
wm.Email = e.EmailAddress
|
||||
wm.UserState = domain.UserStateActive
|
||||
wm.UserState = domain.UserStateInitial
|
||||
case *user.HumanEmailChangedEvent:
|
||||
wm.Email = e.EmailAddress
|
||||
wm.IsEmailVerified = false
|
||||
case *user.HumanEmailVerifiedEvent:
|
||||
wm.IsEmailVerified = true
|
||||
if wm.UserState == domain.UserStateInitial {
|
||||
wm.UserState = domain.UserStateActive
|
||||
}
|
||||
case *user.UserRemovedEvent:
|
||||
wm.UserState = domain.UserStateDeleted
|
||||
}
|
||||
|
52
internal/v2/command/user_human_externalidp.go
Normal file
52
internal/v2/command/user_human_externalidp.go
Normal file
@@ -0,0 +1,52 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/telemetry/tracing"
|
||||
"github.com/caos/zitadel/internal/v2/domain"
|
||||
"github.com/caos/zitadel/internal/v2/repository/user"
|
||||
)
|
||||
|
||||
func (r *CommandSide) RemoveHumanExternalIDP(ctx context.Context, externalIDP *domain.ExternalIDP) error {
|
||||
return r.removeHumanExternalIDP(ctx, externalIDP, false)
|
||||
}
|
||||
|
||||
func (r *CommandSide) removeHumanExternalIDP(ctx context.Context, externalIDP *domain.ExternalIDP, cascade bool) error {
|
||||
if externalIDP.IsValid() {
|
||||
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-3M9ds", "Errors.IDMissing")
|
||||
}
|
||||
|
||||
existingExternalIDP, err := r.externalIDPWriteModelByID(ctx, externalIDP.AggregateID, externalIDP.IDPConfigID, externalIDP.ExternalUserID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if existingExternalIDP.State == domain.ExternalIDPStateUnspecified || existingExternalIDP.State == domain.ExternalIDPStateRemoved {
|
||||
return caos_errs.ThrowNotFound(nil, "COMMAND-5M0ds", "Errors.User.ExternalIDP.NotFound")
|
||||
}
|
||||
userAgg := UserAggregateFromWriteModel(&existingExternalIDP.WriteModel)
|
||||
if !cascade {
|
||||
userAgg.PushEvents(
|
||||
user.NewHumanExternalIDPRemovedEvent(ctx, externalIDP.IDPConfigID, externalIDP.ExternalUserID),
|
||||
)
|
||||
} else {
|
||||
userAgg.PushEvents(
|
||||
user.NewHumanExternalIDPCascadeRemovedEvent(ctx, externalIDP.IDPConfigID, externalIDP.ExternalUserID),
|
||||
)
|
||||
}
|
||||
|
||||
//TODO: Release unique externalidp
|
||||
return r.eventstore.PushAggregate(ctx, existingExternalIDP, userAgg)
|
||||
}
|
||||
|
||||
func (r *CommandSide) externalIDPWriteModelByID(ctx context.Context, userID, idpConfigID, externalUserID string) (writeModel *HumanExternalIDPWriteModel, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
writeModel = NewHumanExternalIDPWriteModel(userID, idpConfigID, externalUserID)
|
||||
err = r.eventstore.FilterToQueryReducer(ctx, writeModel)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return writeModel, nil
|
||||
}
|
72
internal/v2/command/user_human_externalidp_model.go
Normal file
72
internal/v2/command/user_human_externalidp_model.go
Normal file
@@ -0,0 +1,72 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"github.com/caos/zitadel/internal/eventstore/v2"
|
||||
"github.com/caos/zitadel/internal/v2/domain"
|
||||
"github.com/caos/zitadel/internal/v2/repository/user"
|
||||
)
|
||||
|
||||
type HumanExternalIDPWriteModel struct {
|
||||
eventstore.WriteModel
|
||||
|
||||
IDPConfigID string
|
||||
ExternalUserID string
|
||||
DisplayName string
|
||||
|
||||
State domain.ExternalIDPState
|
||||
}
|
||||
|
||||
func NewHumanExternalIDPWriteModel(userID, idpConfigID, externalUserID string) *HumanExternalIDPWriteModel {
|
||||
return &HumanExternalIDPWriteModel{
|
||||
WriteModel: eventstore.WriteModel{
|
||||
AggregateID: userID,
|
||||
},
|
||||
IDPConfigID: idpConfigID,
|
||||
ExternalUserID: externalUserID,
|
||||
}
|
||||
}
|
||||
|
||||
func (wm *HumanExternalIDPWriteModel) AppendEvents(events ...eventstore.EventReader) {
|
||||
for _, event := range events {
|
||||
switch e := event.(type) {
|
||||
case *user.HumanExternalIDPAddedEvent:
|
||||
if wm.IDPConfigID == e.IDPConfigID && wm.ExternalUserID == e.UserID {
|
||||
wm.AppendEvents(e)
|
||||
}
|
||||
case *user.HumanExternalIDPRemovedEvent:
|
||||
if wm.IDPConfigID == e.IDPConfigID && wm.ExternalUserID == e.UserID {
|
||||
wm.AppendEvents(e)
|
||||
}
|
||||
case *user.HumanExternalIDPCascadeRemovedEvent:
|
||||
if wm.IDPConfigID == e.IDPConfigID && wm.ExternalUserID == e.UserID {
|
||||
wm.AppendEvents(e)
|
||||
}
|
||||
case *user.UserRemovedEvent:
|
||||
wm.AppendEvents(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (wm *HumanExternalIDPWriteModel) Reduce() error {
|
||||
for _, event := range wm.Events {
|
||||
switch e := event.(type) {
|
||||
case *user.HumanExternalIDPAddedEvent:
|
||||
wm.IDPConfigID = e.IDPConfigID
|
||||
wm.DisplayName = e.DisplayName
|
||||
wm.ExternalUserID = e.UserID
|
||||
wm.State = domain.ExternalIDPStateActive
|
||||
case *user.HumanExternalIDPRemovedEvent:
|
||||
wm.State = domain.ExternalIDPStateRemoved
|
||||
case *user.HumanExternalIDPCascadeRemovedEvent:
|
||||
wm.State = domain.ExternalIDPStateRemoved
|
||||
case *user.UserRemovedEvent:
|
||||
wm.State = domain.ExternalIDPStateRemoved
|
||||
}
|
||||
}
|
||||
return wm.WriteModel.Reduce()
|
||||
}
|
||||
|
||||
func (wm *HumanExternalIDPWriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, user.AggregateType).
|
||||
AggregateIDs(wm.AggregateID)
|
||||
}
|
@@ -76,6 +76,8 @@ func (wm *HumanWriteModel) Reduce() error {
|
||||
wm.reduceHumanAddedEvent(e)
|
||||
case *user.HumanRegisteredEvent:
|
||||
wm.reduceHumanRegisteredEvent(e)
|
||||
case *user.UsernameChangedEvent:
|
||||
wm.UserName = e.UserName
|
||||
case *user.HumanProfileChangedEvent:
|
||||
wm.reduceHumanProfileChangedEvent(e)
|
||||
case *user.HumanEmailChangedEvent:
|
||||
|
41
internal/v2/command/user_human_otp.go
Normal file
41
internal/v2/command/user_human_otp.go
Normal file
@@ -0,0 +1,41 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/telemetry/tracing"
|
||||
"github.com/caos/zitadel/internal/v2/domain"
|
||||
"github.com/caos/zitadel/internal/v2/repository/user"
|
||||
)
|
||||
|
||||
func (r *CommandSide) RemoveHumanOTP(ctx context.Context, userID string) error {
|
||||
if userID == "" {
|
||||
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-5M0sd", "Errors.User.UserIDMissing")
|
||||
}
|
||||
|
||||
existingOTP, err := r.otpWriteModelByID(ctx, userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if existingOTP.State == domain.OTPStateUnspecified || existingOTP.State == domain.OTPStateRemoved {
|
||||
return caos_errs.ThrowNotFound(nil, "COMMAND-5M0ds", "Errors.User.OTP.NotFound")
|
||||
}
|
||||
userAgg := UserAggregateFromWriteModel(&existingOTP.WriteModel)
|
||||
userAgg.PushEvents(
|
||||
user.NewHumanOTPRemovedEvent(ctx),
|
||||
)
|
||||
|
||||
return r.eventstore.PushAggregate(ctx, existingOTP, userAgg)
|
||||
}
|
||||
|
||||
func (r *CommandSide) otpWriteModelByID(ctx context.Context, userID string) (writeModel *HumanOTPWriteModel, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
writeModel = NewHumanOTPWriteModel(userID)
|
||||
err = r.eventstore.FilterToQueryReducer(ctx, writeModel)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return writeModel, nil
|
||||
}
|
57
internal/v2/command/user_human_otp_model.go
Normal file
57
internal/v2/command/user_human_otp_model.go
Normal file
@@ -0,0 +1,57 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"github.com/caos/zitadel/internal/crypto"
|
||||
"github.com/caos/zitadel/internal/eventstore/v2"
|
||||
"github.com/caos/zitadel/internal/v2/domain"
|
||||
"github.com/caos/zitadel/internal/v2/repository/user"
|
||||
)
|
||||
|
||||
type HumanOTPWriteModel struct {
|
||||
eventstore.WriteModel
|
||||
|
||||
Secret *crypto.CryptoValue
|
||||
|
||||
State domain.OTPState
|
||||
}
|
||||
|
||||
func NewHumanOTPWriteModel(userID string) *HumanOTPWriteModel {
|
||||
return &HumanOTPWriteModel{
|
||||
WriteModel: eventstore.WriteModel{
|
||||
AggregateID: userID,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (wm *HumanOTPWriteModel) AppendEvents(events ...eventstore.EventReader) {
|
||||
for _, event := range events {
|
||||
switch e := event.(type) {
|
||||
case *user.HumanOTPAddedEvent:
|
||||
wm.AppendEvents(e)
|
||||
case *user.HumanOTPRemovedEvent:
|
||||
wm.AppendEvents(e)
|
||||
case *user.UserRemovedEvent:
|
||||
wm.AppendEvents(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (wm *HumanOTPWriteModel) Reduce() error {
|
||||
for _, event := range wm.Events {
|
||||
switch e := event.(type) {
|
||||
case *user.HumanOTPAddedEvent:
|
||||
wm.Secret = e.Secret
|
||||
wm.State = domain.OTPStateActive
|
||||
case *user.HumanOTPRemovedEvent:
|
||||
wm.State = domain.OTPStateRemoved
|
||||
case *user.UserRemovedEvent:
|
||||
wm.State = domain.OTPStateRemoved
|
||||
}
|
||||
}
|
||||
return wm.WriteModel.Reduce()
|
||||
}
|
||||
|
||||
func (wm *HumanOTPWriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, user.AggregateType).
|
||||
AggregateIDs(wm.AggregateID)
|
||||
}
|
107
internal/v2/command/user_human_password.go
Normal file
107
internal/v2/command/user_human_password.go
Normal file
@@ -0,0 +1,107 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/caos/zitadel/internal/crypto"
|
||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/telemetry/tracing"
|
||||
"github.com/caos/zitadel/internal/v2/domain"
|
||||
"github.com/caos/zitadel/internal/v2/repository/user"
|
||||
)
|
||||
|
||||
func (r *CommandSide) SetOneTimePassword(ctx context.Context, orgID, userID, passwordString string) (err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
existingPassword, err := r.passwordWriteModel(ctx, userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
password := &domain.Password{
|
||||
SecretString: passwordString,
|
||||
ChangeRequired: true,
|
||||
}
|
||||
return r.changePassword(ctx, orgID, userID, "", password, existingPassword)
|
||||
}
|
||||
|
||||
func (r *CommandSide) ChangePassword(ctx context.Context, orgID, userID, oldPassword, newPassword, userAgentID string) (err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
existingPassword, err := r.passwordWriteModel(ctx, userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if existingPassword.Secret != nil {
|
||||
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-Fds3s", "Errors.User.Password.Empty")
|
||||
}
|
||||
ctx, spanPasswordComparison := tracing.NewNamedSpan(ctx, "crypto.CompareHash")
|
||||
err = crypto.CompareHash(existingPassword.Secret, []byte(oldPassword), r.userPasswordAlg)
|
||||
spanPasswordComparison.EndWithError(err)
|
||||
|
||||
if err != nil {
|
||||
return caos_errs.ThrowInvalidArgument(nil, "COMMAND-3M0fs", "Errors.User.Password.Invalid")
|
||||
}
|
||||
password := &domain.Password{
|
||||
SecretString: newPassword,
|
||||
ChangeRequired: true,
|
||||
}
|
||||
return r.changePassword(ctx, orgID, userID, userAgentID, password, existingPassword)
|
||||
}
|
||||
|
||||
func (r *CommandSide) changePassword(ctx context.Context, orgID, userID, userAgentID string, password *domain.Password, existingPassword *HumanPasswordWriteModel) (err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
if userID == "" {
|
||||
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-B8hY3", "Errors.User.UserIDMissing")
|
||||
}
|
||||
if existingPassword.UserState == domain.UserStateUnspecified || existingPassword.UserState == domain.UserStateDeleted {
|
||||
return caos_errs.ThrowNotFound(nil, "COMMAND-G8dh3", "Errors.User.Email.NotFound")
|
||||
}
|
||||
if existingPassword.UserState == domain.UserStateInitial {
|
||||
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-M9dse", "Errors.User.NotInitialised")
|
||||
}
|
||||
pwPolicy, err := r.GetOrgPasswordComplexityPolicy(ctx, orgID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := password.HashPasswordIfExisting(pwPolicy, r.userPasswordAlg); err != nil {
|
||||
return err
|
||||
}
|
||||
userAgg := UserAggregateFromWriteModel(&existingPassword.WriteModel)
|
||||
userAgg.PushEvents(user.NewHumanPasswordChangedEvent(ctx, password.SecretCrypto, password.ChangeRequired, userAgentID))
|
||||
return r.eventstore.PushAggregate(ctx, existingPassword, userAgg)
|
||||
}
|
||||
|
||||
func (r *CommandSide) RequestSetPassword(ctx context.Context, userID string, notifyType domain.NotificationType) (err error) {
|
||||
existingHuman, err := r.userWriteModelByID(ctx, userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if existingHuman.UserState == domain.UserStateUnspecified || existingHuman.UserState == domain.UserStateDeleted {
|
||||
return caos_errs.ThrowNotFound(nil, "COMMAND-Hj9ds", "Errors.User.NotFound")
|
||||
}
|
||||
if existingHuman.UserState == domain.UserStateInitial {
|
||||
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2M9sd", "Errors.User.NotInitialised")
|
||||
}
|
||||
userAgg := UserAggregateFromWriteModel(&existingHuman.WriteModel)
|
||||
passwordCode, err := domain.NewPasswordCode(r.passwordVerificationCode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
userAgg.PushEvents(user.NewHumanPasswordCodeAddedEvent(ctx, passwordCode.Code, passwordCode.Expiry, notifyType))
|
||||
return r.eventstore.PushAggregate(ctx, existingHuman, userAgg)
|
||||
}
|
||||
|
||||
func (r *CommandSide) passwordWriteModel(ctx context.Context, userID string) (writeModel *HumanPasswordWriteModel, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
writeModel = NewHumanPasswordWriteModel(userID)
|
||||
err = r.eventstore.FilterToQueryReducer(ctx, writeModel)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return writeModel, nil
|
||||
}
|
70
internal/v2/command/user_human_password_model.go
Normal file
70
internal/v2/command/user_human_password_model.go
Normal file
@@ -0,0 +1,70 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"github.com/caos/zitadel/internal/crypto"
|
||||
"github.com/caos/zitadel/internal/eventstore/v2"
|
||||
"github.com/caos/zitadel/internal/v2/domain"
|
||||
"github.com/caos/zitadel/internal/v2/repository/user"
|
||||
)
|
||||
|
||||
type HumanPasswordWriteModel struct {
|
||||
eventstore.WriteModel
|
||||
|
||||
Secret *crypto.CryptoValue
|
||||
SecretChangeRequired bool
|
||||
|
||||
UserState domain.UserState
|
||||
}
|
||||
|
||||
func NewHumanPasswordWriteModel(userID string) *HumanPasswordWriteModel {
|
||||
return &HumanPasswordWriteModel{
|
||||
WriteModel: eventstore.WriteModel{
|
||||
AggregateID: userID,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (wm *HumanPasswordWriteModel) AppendEvents(events ...eventstore.EventReader) {
|
||||
for _, event := range events {
|
||||
switch e := event.(type) {
|
||||
case *user.HumanPasswordChangedEvent:
|
||||
wm.AppendEvents(e)
|
||||
case *user.HumanAddedEvent, *user.HumanRegisteredEvent:
|
||||
wm.AppendEvents(e)
|
||||
case *user.HumanEmailVerifiedEvent:
|
||||
wm.AppendEvents(e)
|
||||
case *user.UserRemovedEvent:
|
||||
wm.AppendEvents(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (wm *HumanPasswordWriteModel) Reduce() error {
|
||||
for _, event := range wm.Events {
|
||||
switch e := event.(type) {
|
||||
case *user.HumanAddedEvent:
|
||||
wm.Secret = e.Secret
|
||||
wm.SecretChangeRequired = e.ChangeRequired
|
||||
wm.UserState = domain.UserStateInitial
|
||||
case *user.HumanRegisteredEvent:
|
||||
wm.Secret = e.Secret
|
||||
wm.SecretChangeRequired = e.ChangeRequired
|
||||
wm.UserState = domain.UserStateActive
|
||||
case *user.HumanPasswordChangedEvent:
|
||||
wm.Secret = e.Secret
|
||||
wm.SecretChangeRequired = e.ChangeRequired
|
||||
case *user.HumanEmailVerifiedEvent:
|
||||
if wm.UserState == domain.UserStateInitial {
|
||||
wm.UserState = domain.UserStateActive
|
||||
}
|
||||
case *user.UserRemovedEvent:
|
||||
wm.UserState = domain.UserStateDeleted
|
||||
}
|
||||
}
|
||||
return wm.WriteModel.Reduce()
|
||||
}
|
||||
|
||||
func (wm *HumanPasswordWriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, user.AggregateType).
|
||||
AggregateIDs(wm.AggregateID)
|
||||
}
|
101
internal/v2/command/user_human_phone.go
Normal file
101
internal/v2/command/user_human_phone.go
Normal file
@@ -0,0 +1,101 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/telemetry/tracing"
|
||||
"github.com/caos/zitadel/internal/v2/domain"
|
||||
"github.com/caos/zitadel/internal/v2/repository/user"
|
||||
)
|
||||
|
||||
func (r *CommandSide) ChangeHumanPhone(ctx context.Context, phone *domain.Phone) (*domain.Phone, error) {
|
||||
if !phone.IsValid() {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-6M0ds", "Errors.Phone.Invalid")
|
||||
}
|
||||
|
||||
existingPhone, err := r.phoneWriteModel(ctx, phone.AggregateID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if existingPhone.State == domain.PhoneStateUnspecified || existingPhone.State == domain.PhoneStateRemoved {
|
||||
return nil, caos_errs.ThrowNotFound(nil, "COMMAND-5M0ds", "Errors.User.Phone.NotFound")
|
||||
}
|
||||
changedEvent, hasChanged := existingPhone.NewChangedEvent(ctx, phone.PhoneNumber)
|
||||
if !hasChanged {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-wF94r", "Errors.User.Phone.NotChanged")
|
||||
}
|
||||
userAgg := UserAggregateFromWriteModel(&existingPhone.WriteModel)
|
||||
userAgg.PushEvents(changedEvent)
|
||||
|
||||
if phone.IsPhoneVerified {
|
||||
userAgg.PushEvents(user.NewHumanPhoneVerifiedEvent(ctx))
|
||||
} else {
|
||||
phoneCode, err := domain.NewPhoneCode(r.phoneVerificationCode)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
userAgg.PushEvents(user.NewHumanPhoneCodeAddedEvent(ctx, phoneCode.Code, phoneCode.Expiry))
|
||||
}
|
||||
|
||||
err = r.eventstore.PushAggregate(ctx, existingPhone, userAgg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return writeModelToPhone(existingPhone), nil
|
||||
}
|
||||
|
||||
func (r *CommandSide) CreateHumanPhoneVerificationCode(ctx context.Context, userID string) error {
|
||||
if userID == "" {
|
||||
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-4M0ds", "Errors.User.UserIDMissing")
|
||||
}
|
||||
|
||||
existingPhone, err := r.phoneWriteModel(ctx, userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if existingPhone.State == domain.PhoneStateUnspecified || existingPhone.State == domain.PhoneStateRemoved {
|
||||
return caos_errs.ThrowNotFound(nil, "COMMAND-2M9fs", "Errors.User.Phone.NotFound")
|
||||
}
|
||||
if existingPhone.IsPhoneVerified {
|
||||
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2M9sf", "Errors.User.Phone.AlreadyVerified")
|
||||
}
|
||||
userAgg := UserAggregateFromWriteModel(&existingPhone.WriteModel)
|
||||
phoneCode, err := domain.NewPhoneCode(r.phoneVerificationCode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
userAgg.PushEvents(user.NewHumanPhoneCodeAddedEvent(ctx, phoneCode.Code, phoneCode.Expiry))
|
||||
return r.eventstore.PushAggregate(ctx, existingPhone, userAgg)
|
||||
}
|
||||
|
||||
func (r *CommandSide) RemoveHumanPhone(ctx context.Context, userID string) error {
|
||||
if userID == "" {
|
||||
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-6M0ds", "Errors.User.UserIDMissing")
|
||||
}
|
||||
|
||||
existingPhone, err := r.phoneWriteModel(ctx, userID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if existingPhone.State == domain.PhoneStateUnspecified || existingPhone.State == domain.PhoneStateRemoved {
|
||||
return caos_errs.ThrowNotFound(nil, "COMMAND-5M0ds", "Errors.User.Phone.NotFound")
|
||||
}
|
||||
userAgg := UserAggregateFromWriteModel(&existingPhone.WriteModel)
|
||||
userAgg.PushEvents(
|
||||
user.NewHumanPhoneRemovedEvent(ctx),
|
||||
)
|
||||
return r.eventstore.PushAggregate(ctx, existingPhone, userAgg)
|
||||
}
|
||||
|
||||
func (r *CommandSide) phoneWriteModel(ctx context.Context, userID string) (writeModel *HumanPhoneWriteModel, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
writeModel = NewHumanPhoneWriteModel(userID)
|
||||
err = r.eventstore.FilterToQueryReducer(ctx, writeModel)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return writeModel, nil
|
||||
}
|
88
internal/v2/command/user_human_phone_model.go
Normal file
88
internal/v2/command/user_human_phone_model.go
Normal file
@@ -0,0 +1,88 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/caos/zitadel/internal/eventstore/v2"
|
||||
"github.com/caos/zitadel/internal/v2/domain"
|
||||
"github.com/caos/zitadel/internal/v2/repository/user"
|
||||
)
|
||||
|
||||
type HumanPhoneWriteModel struct {
|
||||
eventstore.WriteModel
|
||||
|
||||
Phone string
|
||||
IsPhoneVerified bool
|
||||
|
||||
State domain.PhoneState
|
||||
}
|
||||
|
||||
func NewHumanPhoneWriteModel(userID string) *HumanPhoneWriteModel {
|
||||
return &HumanPhoneWriteModel{
|
||||
WriteModel: eventstore.WriteModel{
|
||||
AggregateID: userID,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (wm *HumanPhoneWriteModel) AppendEvents(events ...eventstore.EventReader) {
|
||||
for _, event := range events {
|
||||
switch e := event.(type) {
|
||||
case *user.HumanAddedEvent, *user.HumanRegisteredEvent:
|
||||
wm.AppendEvents(e)
|
||||
case *user.HumanPhoneChangedEvent:
|
||||
wm.AppendEvents(e)
|
||||
case *user.HumanPhoneVerifiedEvent:
|
||||
wm.AppendEvents(e)
|
||||
case *user.HumanPhoneRemovedEvent:
|
||||
wm.AppendEvents(e)
|
||||
case *user.UserRemovedEvent:
|
||||
wm.AppendEvents(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (wm *HumanPhoneWriteModel) Reduce() error {
|
||||
for _, event := range wm.Events {
|
||||
switch e := event.(type) {
|
||||
case *user.HumanAddedEvent:
|
||||
if e.PhoneNumber != "" {
|
||||
wm.Phone = e.PhoneNumber
|
||||
wm.State = domain.PhoneStateActive
|
||||
}
|
||||
case *user.HumanRegisteredEvent:
|
||||
if e.PhoneNumber != "" {
|
||||
wm.Phone = e.PhoneNumber
|
||||
wm.State = domain.PhoneStateActive
|
||||
}
|
||||
case *user.HumanPhoneChangedEvent:
|
||||
wm.Phone = e.PhoneNumber
|
||||
wm.IsPhoneVerified = false
|
||||
wm.State = domain.PhoneStateActive
|
||||
case *user.HumanPhoneVerifiedEvent:
|
||||
wm.IsPhoneVerified = true
|
||||
case *user.HumanPhoneRemovedEvent:
|
||||
wm.State = domain.PhoneStateRemoved
|
||||
case *user.UserRemovedEvent:
|
||||
wm.State = domain.PhoneStateRemoved
|
||||
}
|
||||
}
|
||||
return wm.WriteModel.Reduce()
|
||||
}
|
||||
|
||||
func (wm *HumanPhoneWriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, user.AggregateType).
|
||||
AggregateIDs(wm.AggregateID)
|
||||
}
|
||||
|
||||
func (wm *HumanPhoneWriteModel) NewChangedEvent(
|
||||
ctx context.Context,
|
||||
phone string,
|
||||
) (*user.HumanPhoneChangedEvent, bool) {
|
||||
hasChanged := false
|
||||
changedEvent := user.NewHumanPhoneChangedEvent(ctx)
|
||||
if wm.Phone != phone {
|
||||
hasChanged = true
|
||||
changedEvent.PhoneNumber = phone
|
||||
}
|
||||
return changedEvent, hasChanged
|
||||
}
|
@@ -8,7 +8,7 @@ import (
|
||||
)
|
||||
|
||||
func (r *CommandSide) ChangeHumanProfile(ctx context.Context, profile *domain.Profile) (*domain.Profile, error) {
|
||||
if !profile.IsValid() {
|
||||
if !profile.IsValid() && profile.AggregateID != "" {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-8io0d", "Errors.User.Profile.Invalid")
|
||||
}
|
||||
|
||||
@@ -17,11 +17,11 @@ func (r *CommandSide) ChangeHumanProfile(ctx context.Context, profile *domain.Pr
|
||||
return nil, err
|
||||
}
|
||||
if existingProfile.UserState == domain.UserStateUnspecified || existingProfile.UserState == domain.UserStateDeleted {
|
||||
return nil, caos_errs.ThrowAlreadyExists(nil, "COMMAND-3M9sd", "Errors.User.Profile.NotFound")
|
||||
return nil, caos_errs.ThrowNotFound(nil, "COMMAND-3M9sd", "Errors.User.Profile.NotFound")
|
||||
}
|
||||
changedEvent, hasChanged := existingProfile.NewChangedEvent(ctx, profile.FirstName, profile.LastName, profile.NickName, profile.DisplayName, profile.PreferredLanguage, domain.Gender(profile.Gender))
|
||||
if !hasChanged {
|
||||
return nil, caos_errs.ThrowAlreadyExists(nil, "COMMAND-2M0fs", "Errors.User.Profile.NotChanged")
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2M0fs", "Errors.User.Profile.NotChanged")
|
||||
}
|
||||
userAgg := UserAggregateFromWriteModel(&existingProfile.WriteModel)
|
||||
userAgg.PushEvents(changedEvent)
|
||||
|
50
internal/v2/command/user_human_webauthn.go
Normal file
50
internal/v2/command/user_human_webauthn.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/eventstore/v2"
|
||||
"github.com/caos/zitadel/internal/telemetry/tracing"
|
||||
"github.com/caos/zitadel/internal/v2/domain"
|
||||
"github.com/caos/zitadel/internal/v2/repository/user"
|
||||
)
|
||||
|
||||
func (r *CommandSide) RemoveHumanU2F(ctx context.Context, userID, webAuthNID string) error {
|
||||
event := user.NewHumanU2FRemovedEvent(ctx, webAuthNID)
|
||||
return r.removeHumanWebAuthN(ctx, userID, webAuthNID, event)
|
||||
}
|
||||
|
||||
func (r *CommandSide) RemoveHumanPasswordless(ctx context.Context, userID, webAuthNID string) error {
|
||||
event := user.NewHumanPasswordlessRemovedEvent(ctx, webAuthNID)
|
||||
return r.removeHumanWebAuthN(ctx, userID, webAuthNID, event)
|
||||
}
|
||||
|
||||
func (r *CommandSide) removeHumanWebAuthN(ctx context.Context, userID, webAuthNID string, event eventstore.EventPusher) error {
|
||||
if userID == "" || webAuthNID == "" {
|
||||
return caos_errs.ThrowPreconditionFailed(nil, "COMMAND-6M9de", "Errors.IDMissing")
|
||||
}
|
||||
|
||||
existingWebAuthN, err := r.webauthNWriteModelByID(ctx, userID, webAuthNID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if existingWebAuthN.State == domain.WebAuthNStateUnspecified || existingWebAuthN.State == domain.WebAuthNStateRemoved {
|
||||
return caos_errs.ThrowNotFound(nil, "COMMAND-5M0ds", "Errors.User.ExternalIDP.NotFound")
|
||||
}
|
||||
userAgg := UserAggregateFromWriteModel(&existingWebAuthN.WriteModel)
|
||||
userAgg.PushEvents(event)
|
||||
|
||||
return r.eventstore.PushAggregate(ctx, existingWebAuthN, userAgg)
|
||||
}
|
||||
|
||||
func (r *CommandSide) webauthNWriteModelByID(ctx context.Context, userID, webAuthNID string) (writeModel *HumanWebAuthNWriteModel, err error) {
|
||||
ctx, span := tracing.NewSpan(ctx)
|
||||
defer func() { span.EndWithError(err) }()
|
||||
|
||||
writeModel = NewHumanWebAuthNWriteModel(userID, webAuthNID)
|
||||
err = r.eventstore.FilterToQueryReducer(ctx, writeModel)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return writeModel, nil
|
||||
}
|
61
internal/v2/command/user_human_webauthn_model.go
Normal file
61
internal/v2/command/user_human_webauthn_model.go
Normal file
@@ -0,0 +1,61 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"github.com/caos/zitadel/internal/eventstore/v2"
|
||||
"github.com/caos/zitadel/internal/v2/domain"
|
||||
"github.com/caos/zitadel/internal/v2/repository/user"
|
||||
)
|
||||
|
||||
type HumanWebAuthNWriteModel struct {
|
||||
eventstore.WriteModel
|
||||
|
||||
WebauthNTokenID string
|
||||
|
||||
State domain.WebAuthNState
|
||||
}
|
||||
|
||||
func NewHumanWebAuthNWriteModel(userID, wbAuthNTokenID string) *HumanWebAuthNWriteModel {
|
||||
return &HumanWebAuthNWriteModel{
|
||||
WriteModel: eventstore.WriteModel{
|
||||
AggregateID: userID,
|
||||
},
|
||||
WebauthNTokenID: wbAuthNTokenID,
|
||||
}
|
||||
}
|
||||
|
||||
func (wm *HumanWebAuthNWriteModel) AppendEvents(events ...eventstore.EventReader) {
|
||||
for _, event := range events {
|
||||
switch e := event.(type) {
|
||||
case *user.HumanWebAuthNAddedEvent:
|
||||
if wm.WebauthNTokenID == e.WebAuthNTokenID {
|
||||
wm.AppendEvents(e)
|
||||
}
|
||||
case *user.HumanWebAuthNRemovedEvent:
|
||||
if wm.WebauthNTokenID == e.WebAuthNTokenID {
|
||||
wm.AppendEvents(e)
|
||||
}
|
||||
case *user.UserRemovedEvent:
|
||||
wm.AppendEvents(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (wm *HumanWebAuthNWriteModel) Reduce() error {
|
||||
for _, event := range wm.Events {
|
||||
switch e := event.(type) {
|
||||
case *user.HumanWebAuthNAddedEvent:
|
||||
wm.WebauthNTokenID = e.WebAuthNTokenID
|
||||
wm.State = domain.WebAuthNStateActive
|
||||
case *user.HumanWebAuthNRemovedEvent:
|
||||
wm.State = domain.WebAuthNStateRemoved
|
||||
case *user.UserRemovedEvent:
|
||||
wm.State = domain.WebAuthNStateRemoved
|
||||
}
|
||||
}
|
||||
return wm.WriteModel.Reduce()
|
||||
}
|
||||
|
||||
func (wm *HumanWebAuthNWriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, user.AggregateType).
|
||||
AggregateIDs(wm.AggregateID)
|
||||
}
|
@@ -45,12 +45,12 @@ func (r *CommandSide) ChangeMachine(ctx context.Context, machine *domain.Machine
|
||||
return nil, err
|
||||
}
|
||||
if existingUser.UserState == domain.UserStateDeleted || existingUser.UserState == domain.UserStateUnspecified {
|
||||
return nil, caos_errs.ThrowAlreadyExists(nil, "COMMAND-5M0od", "Errors.User.NotFound")
|
||||
return nil, caos_errs.ThrowNotFound(nil, "COMMAND-5M0od", "Errors.User.NotFound")
|
||||
}
|
||||
|
||||
changedEvent, hasChanged := existingUser.NewChangedEvent(ctx, machine.Name, machine.Description)
|
||||
if !hasChanged {
|
||||
return nil, caos_errs.ThrowAlreadyExists(nil, "COMMAND-2M9fs", "Errors.User.Email.NotChanged")
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2M9fs", "Errors.User.Email.NotChanged")
|
||||
}
|
||||
userAgg := UserAggregateFromWriteModel(&existingUser.WriteModel)
|
||||
userAgg.PushEvents(changedEvent)
|
||||
|
@@ -30,6 +30,8 @@ func (wm *MachineWriteModel) AppendEvents(events ...eventstore.EventReader) {
|
||||
switch e := event.(type) {
|
||||
case *user.MachineAddedEvent:
|
||||
wm.AppendEvents(e)
|
||||
case *user.UsernameChangedEvent:
|
||||
wm.AppendEvents(e)
|
||||
case *user.MachineChangedEvent:
|
||||
wm.AppendEvents(e)
|
||||
case *user.UserDeactivatedEvent:
|
||||
@@ -55,6 +57,8 @@ func (wm *MachineWriteModel) Reduce() error {
|
||||
wm.Name = e.Name
|
||||
wm.Description = e.Description
|
||||
wm.UserState = domain.UserStateActive
|
||||
case *user.UsernameChangedEvent:
|
||||
wm.UserName = e.UserName
|
||||
case *user.MachineChangedEvent:
|
||||
if e.Name != nil {
|
||||
wm.Name = *e.Name
|
@@ -1,9 +1,11 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
caos_errors "github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/eventstore/v2"
|
||||
"github.com/caos/zitadel/internal/v2/domain"
|
||||
"github.com/caos/zitadel/internal/v2/repository/user"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type UserWriteModel struct {
|
||||
@@ -24,14 +26,12 @@ func NewUserWriteModel(userID string) *UserWriteModel {
|
||||
func (wm *UserWriteModel) AppendEvents(events ...eventstore.EventReader) {
|
||||
for _, event := range events {
|
||||
switch e := event.(type) {
|
||||
case *user.HumanEmailChangedEvent:
|
||||
wm.AppendEvents(e)
|
||||
case *user.HumanEmailVerifiedEvent:
|
||||
wm.AppendEvents(e)
|
||||
case *user.HumanAddedEvent, *user.HumanRegisteredEvent:
|
||||
wm.AppendEvents(e)
|
||||
case *user.MachineAddedEvent:
|
||||
wm.AppendEvents(e)
|
||||
case *user.UsernameChangedEvent:
|
||||
wm.AppendEvents(e)
|
||||
case *user.UserDeactivatedEvent:
|
||||
wm.AppendEvents(e)
|
||||
case *user.UserReactivatedEvent:
|
||||
@@ -56,10 +56,11 @@ func (wm *UserWriteModel) Reduce() error {
|
||||
case *user.HumanRegisteredEvent:
|
||||
wm.UserName = e.UserName
|
||||
wm.UserState = domain.UserStateInitial
|
||||
|
||||
case *user.MachineAddedEvent:
|
||||
wm.UserName = e.UserName
|
||||
wm.UserState = domain.UserStateActive
|
||||
case *user.UsernameChangedEvent:
|
||||
wm.UserName = e.UserName
|
||||
case *user.UserLockedEvent:
|
||||
if wm.UserState != domain.UserStateDeleted {
|
||||
wm.UserState = domain.UserStateLocked
|
||||
@@ -93,3 +94,13 @@ func UserAggregateFromWriteModel(wm *eventstore.WriteModel) *user.Aggregate {
|
||||
Aggregate: *eventstore.AggregateFromWriteModel(wm, user.AggregateType, user.AggregateVersion),
|
||||
}
|
||||
}
|
||||
|
||||
func CheckOrgIAMPolicyForUserName(userName string, policy *domain.OrgIAMPolicy) error {
|
||||
if policy == nil {
|
||||
return caos_errors.ThrowPreconditionFailed(nil, "COMMAND-3Mb9s", "Errors.Users.OrgIamPolicyNil")
|
||||
}
|
||||
if policy.UserLoginMustBeDomain && strings.Contains(userName, "@") {
|
||||
return caos_errors.ThrowPreconditionFailed(nil, "COMMAND-4M9vs", "Errors.User.EmailAsUsernameNotAllowed")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@@ -14,3 +14,17 @@ const (
|
||||
MultiFactorTypeUnspecified MultiFactorType = iota
|
||||
MultiFactorTypeU2FWithPIN
|
||||
)
|
||||
|
||||
type FactorState int32
|
||||
|
||||
const (
|
||||
FactorStateUnspecified FactorState = iota
|
||||
FactorStateActive
|
||||
FactorStateRemoved
|
||||
|
||||
factorStateCount
|
||||
)
|
||||
|
||||
func (f FactorState) Valid() bool {
|
||||
return f >= 0 && f < factorStateCount
|
||||
}
|
||||
|
@@ -71,7 +71,8 @@ func (u *Human) SetNamesAsDisplayname() {
|
||||
|
||||
func (u *Human) HashPasswordIfExisting(policy *PasswordComplexityPolicy, passwordAlg crypto.HashAlgorithm, onetime bool) error {
|
||||
if u.Password != nil {
|
||||
return u.Password.HashPasswordIfExisting(policy, passwordAlg, onetime)
|
||||
u.Password.ChangeRequired = onetime
|
||||
return u.Password.HashPasswordIfExisting(policy, passwordAlg)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@@ -11,3 +11,17 @@ type Address struct {
|
||||
Region string
|
||||
StreetAddress string
|
||||
}
|
||||
|
||||
type AddressState int32
|
||||
|
||||
const (
|
||||
AddressStateUnspecified AddressState = iota
|
||||
AddressStateActive
|
||||
AddressStateRemoved
|
||||
|
||||
addressStateCount
|
||||
)
|
||||
|
||||
func (s AddressState) Valid() bool {
|
||||
return s >= 0 && s < addressStateCount
|
||||
}
|
||||
|
@@ -23,3 +23,14 @@ type EmailCode struct {
|
||||
func (e *Email) IsValid() bool {
|
||||
return e.EmailAddress != ""
|
||||
}
|
||||
|
||||
func NewEmailCode(emailGenerator crypto.Generator) (*EmailCode, error) {
|
||||
emailCodeCrypto, _, err := crypto.NewCode(emailGenerator)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &EmailCode{
|
||||
Code: emailCodeCrypto,
|
||||
Expiry: emailGenerator.Expiry(),
|
||||
}, nil
|
||||
}
|
||||
|
@@ -5,7 +5,25 @@ import es_models "github.com/caos/zitadel/internal/eventstore/models"
|
||||
type ExternalIDP struct {
|
||||
es_models.ObjectRoot
|
||||
|
||||
IDPConfigID string
|
||||
UserID string
|
||||
DisplayName string
|
||||
IDPConfigID string
|
||||
ExternalUserID string
|
||||
DisplayName string
|
||||
}
|
||||
|
||||
func (idp *ExternalIDP) IsValid() bool {
|
||||
return idp.AggregateID != "" && idp.IDPConfigID != "" && idp.ExternalUserID != ""
|
||||
}
|
||||
|
||||
type ExternalIDPState int32
|
||||
|
||||
const (
|
||||
ExternalIDPStateUnspecified ExternalIDPState = iota
|
||||
ExternalIDPStateActive
|
||||
ExternalIDPStateRemoved
|
||||
|
||||
externalIDPStateCount
|
||||
)
|
||||
|
||||
func (s ExternalIDPState) Valid() bool {
|
||||
return s >= 0 && s < externalIDPStateCount
|
||||
}
|
||||
|
@@ -13,3 +13,17 @@ type OTP struct {
|
||||
Url string
|
||||
State MFAState
|
||||
}
|
||||
|
||||
type OTPState int32
|
||||
|
||||
const (
|
||||
OTPStateUnspecified OTPState = iota
|
||||
OTPStateActive
|
||||
OTPStateRemoved
|
||||
|
||||
otpStateCount
|
||||
)
|
||||
|
||||
func (s OTPState) Valid() bool {
|
||||
return s >= 0 && s < otpStateCount
|
||||
}
|
||||
|
@@ -23,7 +23,7 @@ type PasswordCode struct {
|
||||
NotificationType NotificationType
|
||||
}
|
||||
|
||||
func (p *Password) HashPasswordIfExisting(policy *PasswordComplexityPolicy, passwordAlg crypto.HashAlgorithm, onetime bool) error {
|
||||
func (p *Password) HashPasswordIfExisting(policy *PasswordComplexityPolicy, passwordAlg crypto.HashAlgorithm) error {
|
||||
if p.SecretString == "" {
|
||||
return nil
|
||||
}
|
||||
@@ -38,6 +38,16 @@ func (p *Password) HashPasswordIfExisting(policy *PasswordComplexityPolicy, pass
|
||||
return err
|
||||
}
|
||||
p.SecretCrypto = secret
|
||||
p.ChangeRequired = onetime
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewPasswordCode(passwordGenerator crypto.Generator) (*PasswordCode, error) {
|
||||
passwordCodeCrypto, _, err := crypto.NewCode(passwordGenerator)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &PasswordCode{
|
||||
Code: passwordCodeCrypto,
|
||||
Expiry: passwordGenerator.Expiry(),
|
||||
}, nil
|
||||
}
|
||||
|
@@ -50,3 +50,17 @@ func NewPhoneCode(phoneGenerator crypto.Generator) (*PhoneCode, error) {
|
||||
Expiry: phoneGenerator.Expiry(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
type PhoneState int32
|
||||
|
||||
const (
|
||||
PhoneStateUnspecified PhoneState = iota
|
||||
PhoneStateActive
|
||||
PhoneStateRemoved
|
||||
|
||||
phoneStateCount
|
||||
)
|
||||
|
||||
func (s PhoneState) Valid() bool {
|
||||
return s >= 0 && s < phoneStateCount
|
||||
}
|
||||
|
@@ -38,3 +38,17 @@ const (
|
||||
UserVerificationRequirementPreferred
|
||||
UserVerificationRequirementDiscouraged
|
||||
)
|
||||
|
||||
type WebAuthNState int32
|
||||
|
||||
const (
|
||||
WebAuthNStateUnspecified WebAuthNState = iota
|
||||
WebAuthNStateActive
|
||||
WebAuthNStateRemoved
|
||||
|
||||
webAuthNStateCount
|
||||
)
|
||||
|
||||
func (s WebAuthNState) Valid() bool {
|
||||
return s >= 0 && s < webAuthNStateCount
|
||||
}
|
||||
|
@@ -14,3 +14,17 @@ type IAMMember struct {
|
||||
func (i *IAMMember) IsValid() bool {
|
||||
return i.AggregateID != "" && i.UserID != "" && len(i.Roles) != 0
|
||||
}
|
||||
|
||||
type MemberState int32
|
||||
|
||||
const (
|
||||
MemberStateUnspecified MemberState = iota
|
||||
MemberStateActive
|
||||
MemberStateRemoved
|
||||
|
||||
memberStateCount
|
||||
)
|
||||
|
||||
func (f MemberState) Valid() bool {
|
||||
return f >= 0 && f < memberStateCount
|
||||
}
|
||||
|
15
internal/v2/domain/policy.go
Normal file
15
internal/v2/domain/policy.go
Normal file
@@ -0,0 +1,15 @@
|
||||
package domain
|
||||
|
||||
type PolicyState int32
|
||||
|
||||
const (
|
||||
PolicyStateUnspecified PolicyState = iota
|
||||
PolicyStateActive
|
||||
PolicyStateRemoved
|
||||
|
||||
policyStateCount
|
||||
)
|
||||
|
||||
func (f PolicyState) Valid() bool {
|
||||
return f >= 0 && f < policyStateCount
|
||||
}
|
@@ -12,3 +12,17 @@ const (
|
||||
func (f IdentityProviderType) Valid() bool {
|
||||
return f >= 0 && f < identityProviderCount
|
||||
}
|
||||
|
||||
type IdentityProviderState int32
|
||||
|
||||
const (
|
||||
IdentityProviderStateUnspecified IdentityProviderState = iota
|
||||
IdentityProviderStateActive
|
||||
IdentityProviderStateRemoved
|
||||
|
||||
idpProviderState
|
||||
)
|
||||
|
||||
func (s IdentityProviderState) Valid() bool {
|
||||
return s >= 0 && s < idpProviderState
|
||||
}
|
||||
|
@@ -61,6 +61,7 @@ type HumanExternalIDPAddedEvent struct {
|
||||
eventstore.BaseEvent `json:"-"`
|
||||
|
||||
IDPConfigID string `json:"idpConfigId,omitempty"`
|
||||
UserID string `json:"userId,omitempty"`
|
||||
DisplayName string `json:"displayName,omitempty"`
|
||||
}
|
||||
|
||||
@@ -96,19 +97,21 @@ type HumanExternalIDPRemovedEvent struct {
|
||||
eventstore.BaseEvent `json:"-"`
|
||||
|
||||
IDPConfigID string `json:"idpConfigId"`
|
||||
UserID string `json:"userId,omitempty"`
|
||||
}
|
||||
|
||||
func (e *HumanExternalIDPRemovedEvent) Data() interface{} {
|
||||
return e
|
||||
}
|
||||
|
||||
func NewHumanExternalIDPRemovedEvent(ctx context.Context, idpConfigID string) *HumanExternalIDPRemovedEvent {
|
||||
func NewHumanExternalIDPRemovedEvent(ctx context.Context, idpConfigID, externalUserID string) *HumanExternalIDPRemovedEvent {
|
||||
return &HumanExternalIDPRemovedEvent{
|
||||
BaseEvent: *eventstore.NewBaseEventForPush(
|
||||
ctx,
|
||||
HumanExternalIDPRemovedType,
|
||||
),
|
||||
IDPConfigID: idpConfigID,
|
||||
UserID: externalUserID,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -129,19 +132,21 @@ type HumanExternalIDPCascadeRemovedEvent struct {
|
||||
eventstore.BaseEvent `json:"-"`
|
||||
|
||||
IDPConfigID string `json:"idpConfigId"`
|
||||
UserID string `json:"userId,omitempty"`
|
||||
}
|
||||
|
||||
func (e *HumanExternalIDPCascadeRemovedEvent) Data() interface{} {
|
||||
return e
|
||||
}
|
||||
|
||||
func NewHumanExternalIDPCascadeRemovedEvent(ctx context.Context, idpConfigID string) *HumanExternalIDPCascadeRemovedEvent {
|
||||
func NewHumanExternalIDPCascadeRemovedEvent(ctx context.Context, idpConfigID, externalUserID string) *HumanExternalIDPCascadeRemovedEvent {
|
||||
return &HumanExternalIDPCascadeRemovedEvent{
|
||||
BaseEvent: *eventstore.NewBaseEventForPush(
|
||||
ctx,
|
||||
HumanExternalIDPCascadeRemovedType,
|
||||
),
|
||||
IDPConfigID: idpConfigID,
|
||||
UserID: externalUserID,
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -7,7 +7,6 @@ import (
|
||||
"github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/eventstore/v2"
|
||||
"github.com/caos/zitadel/internal/eventstore/v2/repository"
|
||||
"github.com/caos/zitadel/internal/v2/domain"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -23,7 +22,6 @@ type HumanOTPAddedEvent struct {
|
||||
eventstore.BaseEvent `json:"-"`
|
||||
|
||||
Secret *crypto.CryptoValue `json:"otpSecret,omitempty"`
|
||||
State domain.MFAState `json:"-"`
|
||||
}
|
||||
|
||||
func (e *HumanOTPAddedEvent) Data() interface{} {
|
||||
@@ -44,7 +42,6 @@ func NewHumanOTPAddedEvent(ctx context.Context,
|
||||
func HumanOTPAddedEventMapper(event *repository.Event) (eventstore.EventReader, error) {
|
||||
otpAdded := &HumanOTPAddedEvent{
|
||||
BaseEvent: *eventstore.BaseEventFromRepo(event),
|
||||
State: domain.MFAStateNotReady,
|
||||
}
|
||||
err := json.Unmarshal(event.Data, otpAdded)
|
||||
if err != nil {
|
||||
@@ -55,7 +52,6 @@ func HumanOTPAddedEventMapper(event *repository.Event) (eventstore.EventReader,
|
||||
|
||||
type HumanOTPVerifiedEvent struct {
|
||||
eventstore.BaseEvent `json:"-"`
|
||||
State domain.MFAState `json:"-"`
|
||||
}
|
||||
|
||||
func (e *HumanOTPVerifiedEvent) Data() interface{} {
|
||||
@@ -74,7 +70,6 @@ func NewHumanOTPVerifiedEvent(ctx context.Context) *HumanOTPVerifiedEvent {
|
||||
func HumanOTPVerifiedEventMapper(event *repository.Event) (eventstore.EventReader, error) {
|
||||
return &HumanOTPVerifiedEvent{
|
||||
BaseEvent: *eventstore.BaseEventFromRepo(event),
|
||||
State: domain.MFAStateReady,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
@@ -25,6 +25,7 @@ type HumanPasswordChangedEvent struct {
|
||||
|
||||
Secret *crypto.CryptoValue `json:"secret,omitempty"`
|
||||
ChangeRequired bool `json:"changeRequired"`
|
||||
UserAgentID string `json:"userAgentID,omitempty"`
|
||||
}
|
||||
|
||||
func (e *HumanPasswordChangedEvent) Data() interface{} {
|
||||
@@ -35,6 +36,7 @@ func NewHumanPasswordChangedEvent(
|
||||
ctx context.Context,
|
||||
secret *crypto.CryptoValue,
|
||||
changeRequired bool,
|
||||
userAgentID string,
|
||||
) *HumanPasswordChangedEvent {
|
||||
return &HumanPasswordChangedEvent{
|
||||
BaseEvent: *eventstore.NewBaseEventForPush(
|
||||
@@ -43,6 +45,7 @@ func NewHumanPasswordChangedEvent(
|
||||
),
|
||||
Secret: secret,
|
||||
ChangeRequired: changeRequired,
|
||||
UserAgentID: userAgentID,
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -30,13 +30,12 @@ func (e *HumanPhoneChangedEvent) Data() interface{} {
|
||||
return e
|
||||
}
|
||||
|
||||
func NewHumanPhoneChangedEvent(ctx context.Context, phone string) *HumanPhoneChangedEvent {
|
||||
func NewHumanPhoneChangedEvent(ctx context.Context) *HumanPhoneChangedEvent {
|
||||
return &HumanPhoneChangedEvent{
|
||||
BaseEvent: *eventstore.NewBaseEventForPush(
|
||||
ctx,
|
||||
HumanPhoneChangedType,
|
||||
),
|
||||
PhoneNumber: phone,
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user