new pkg structure (#1150)

* fix: split command query side

* fix: split command query side

* fix: members in correct pkg structure

* fix: label policy in correct pkg structure

* fix: structure

* fix: structure of login policy

* fix: identityprovider structure

* fix: org iam policy structure

* fix: password age policy structure

* fix: password complexity policy structure

* fix: password lockout policy structure

* fix: idp structure

* fix: user events structure

* fix: user write model

* fix: profile email changed command

* fix: address changed command

* fix: user states

* fix: user

* fix: org structure and add human

* begin iam setup command side

* setup

* step2

* step2

* fix: add user

* step2

* isvalid

* fix: folder structure v2 business

Co-authored-by: Fabiennne <[email protected]>
This commit is contained in:
Livio Amstutz
2021-01-04 14:52:13 +01:00
committed by GitHub
co-authored by Fabiennne
parent 762941f0ea
commit 21ffe1b0cb
260 changed files with 7917 additions and 6570 deletions
+72
View File
@@ -0,0 +1,72 @@
package command
import (
"context"
sd "github.com/caos/zitadel/internal/config/systemdefaults"
"github.com/caos/zitadel/internal/crypto"
"github.com/caos/zitadel/internal/eventstore/v2"
"github.com/caos/zitadel/internal/id"
"github.com/caos/zitadel/internal/telemetry/tracing"
iam_repo "github.com/caos/zitadel/internal/v2/repository/iam"
)
type CommandSide struct {
eventstore *eventstore.Eventstore
idGenerator id.Generator
iamID string
idpConfigSecretCrypto crypto.Crypto
userPasswordAlg crypto.HashAlgorithm
initializeUserCode crypto.Generator
emailVerificationCode crypto.Generator
phoneVerificationCode crypto.Generator
passwordVerificationCode crypto.Generator
machineKeyAlg crypto.EncryptionAlgorithm
machineKeySize int
}
type Config struct {
Eventstore *eventstore.Eventstore
SystemDefaults sd.SystemDefaults
}
func StartCommandSide(config *Config) (repo *CommandSide, err error) {
repo = &CommandSide{
eventstore: config.Eventstore,
idGenerator: id.SonyFlakeGenerator,
iamID: config.SystemDefaults.IamID,
}
iam_repo.RegisterEventMappers(repo.eventstore)
repo.idpConfigSecretCrypto, err = crypto.NewAESCrypto(config.SystemDefaults.IDPConfigVerificationKey)
if err != nil {
return nil, err
}
aesCrypto, err := crypto.NewAESCrypto(config.SystemDefaults.UserVerificationKey)
if err != nil {
return nil, err
}
repo.initializeUserCode = crypto.NewEncryptionGenerator(config.SystemDefaults.SecretGenerators.InitializeUserCode, aesCrypto)
repo.emailVerificationCode = crypto.NewEncryptionGenerator(config.SystemDefaults.SecretGenerators.EmailVerificationCode, aesCrypto)
repo.phoneVerificationCode = crypto.NewEncryptionGenerator(config.SystemDefaults.SecretGenerators.PhoneVerificationCode, aesCrypto)
repo.passwordVerificationCode = crypto.NewEncryptionGenerator(config.SystemDefaults.SecretGenerators.PasswordVerificationCode, aesCrypto)
repo.userPasswordAlg = crypto.NewBCrypt(config.SystemDefaults.SecretGenerators.PasswordSaltCost)
repo.machineKeyAlg = aesCrypto
repo.machineKeySize = int(config.SystemDefaults.SecretGenerators.MachineKeySize)
return repo, nil
}
func (r *CommandSide) iamByID(ctx context.Context, id string) (_ *IAMWriteModel, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
writeModel := NewIAMriteModel(id)
err = r.eventstore.FilterToQueryReducer(ctx, writeModel)
if err != nil {
return nil, err
}
return writeModel, nil
}
+118
View File
@@ -0,0 +1,118 @@
package command
import (
"github.com/caos/zitadel/internal/eventstore/models"
"github.com/caos/zitadel/internal/eventstore/v2"
"github.com/caos/zitadel/internal/iam/model"
)
func writeModelToObjectRoot(writeModel eventstore.WriteModel) models.ObjectRoot {
return models.ObjectRoot{
AggregateID: writeModel.AggregateID,
ChangeDate: writeModel.ChangeDate,
ResourceOwner: writeModel.ResourceOwner,
Sequence: writeModel.ProcessedSequence,
}
}
func writeModelToIAM(wm *IAMWriteModel) *model.IAM {
return &model.IAM{
ObjectRoot: writeModelToObjectRoot(wm.WriteModel),
SetUpStarted: wm.SetUpStarted,
SetUpDone: wm.SetUpDone,
GlobalOrgID: wm.GlobalOrgID,
IAMProjectID: wm.ProjectID,
}
}
func writeModelToMember(writeModel *IAMMemberWriteModel) *model.IAMMember {
return &model.IAMMember{
ObjectRoot: writeModelToObjectRoot(writeModel.MemberWriteModel.WriteModel),
Roles: writeModel.Roles,
UserID: writeModel.UserID,
}
}
func writeModelToLoginPolicy(wm *IAMLoginPolicyWriteModel) *model.LoginPolicy {
return &model.LoginPolicy{
ObjectRoot: writeModelToObjectRoot(wm.LoginPolicyWriteModel.WriteModel),
AllowUsernamePassword: wm.AllowUserNamePassword,
AllowRegister: wm.AllowRegister,
AllowExternalIdp: wm.AllowExternalIDP,
ForceMFA: wm.ForceMFA,
PasswordlessType: model.PasswordlessType(wm.PasswordlessType),
}
}
func writeModelToLabelPolicy(wm *IAMLabelPolicyWriteModel) *model.LabelPolicy {
return &model.LabelPolicy{
ObjectRoot: writeModelToObjectRoot(wm.LabelPolicyWriteModel.WriteModel),
PrimaryColor: wm.PrimaryColor,
SecondaryColor: wm.SecondaryColor,
}
}
func writeModelToOrgIAMPolicy(wm *IAMOrgIAMPolicyWriteModel) *model.OrgIAMPolicy {
return &model.OrgIAMPolicy{
ObjectRoot: writeModelToObjectRoot(wm.PolicyOrgIAMWriteModel.WriteModel),
UserLoginMustBeDomain: wm.UserLoginMustBeDomain,
}
}
func writeModelToPasswordAgePolicy(wm *IAMPasswordAgePolicyWriteModel) *model.PasswordAgePolicy {
return &model.PasswordAgePolicy{
ObjectRoot: writeModelToObjectRoot(wm.PasswordAgePolicyWriteModel.WriteModel),
MaxAgeDays: wm.MaxAgeDays,
ExpireWarnDays: wm.ExpireWarnDays,
}
}
func writeModelToPasswordComplexityPolicy(wm *IAMPasswordComplexityPolicyWriteModel) *model.PasswordComplexityPolicy {
return &model.PasswordComplexityPolicy{
ObjectRoot: writeModelToObjectRoot(wm.PasswordComplexityPolicyWriteModel.WriteModel),
MinLength: wm.MinLength,
HasLowercase: wm.HasLowercase,
HasUppercase: wm.HasUpperCase,
HasNumber: wm.HasNumber,
HasSymbol: wm.HasSymbol,
}
}
func writeModelToPasswordLockoutPolicy(wm *IAMPasswordLockoutPolicyWriteModel) *model.PasswordLockoutPolicy {
return &model.PasswordLockoutPolicy{
ObjectRoot: writeModelToObjectRoot(wm.PasswordLockoutPolicyWriteModel.WriteModel),
MaxAttempts: wm.MaxAttempts,
ShowLockOutFailures: wm.ShowLockOutFailures,
}
}
func writeModelToIDPConfig(wm *IAMIDPConfigWriteModel) *model.IDPConfig {
return &model.IDPConfig{
ObjectRoot: writeModelToObjectRoot(wm.WriteModel),
OIDCConfig: writeModelToIDPOIDCConfig(wm.OIDCConfig),
IDPConfigID: wm.ConfigID,
Name: wm.Name,
State: model.IDPConfigState(wm.State),
StylingType: model.IDPStylingType(wm.StylingType),
}
}
func writeModelToIDPOIDCConfig(wm *OIDCConfigWriteModel) *model.OIDCIDPConfig {
return &model.OIDCIDPConfig{
ObjectRoot: writeModelToObjectRoot(wm.WriteModel),
ClientID: wm.ClientID,
IDPConfigID: wm.IDPConfigID,
IDPDisplayNameMapping: model.OIDCMappingField(wm.IDPDisplayNameMapping),
Issuer: wm.Issuer,
Scopes: wm.Scopes,
UsernameMapping: model.OIDCMappingField(wm.UserNameMapping),
}
}
func writeModelToIDPProvider(wm *IAMIdentityProviderWriteModel) *model.IDPProvider {
return &model.IDPProvider{
ObjectRoot: writeModelToObjectRoot(wm.IdentityProviderWriteModel.WriteModel),
IDPConfigID: wm.IDPConfigID,
Type: model.IDPProviderType(wm.IDPProviderType),
}
}
+159
View File
@@ -0,0 +1,159 @@
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/crypto"
"github.com/caos/zitadel/internal/errors"
iam_model "github.com/caos/zitadel/internal/iam/model"
"github.com/caos/zitadel/internal/v2/repository/iam"
iam_repo "github.com/caos/zitadel/internal/v2/repository/iam"
)
func (r *CommandSide) AddDefaultIDPConfig(ctx context.Context, config *iam_model.IDPConfig) (*iam_model.IDPConfig, error) {
if config.OIDCConfig == nil {
return nil, errors.ThrowInvalidArgument(nil, "IAM-eUpQU", "Errors.idp.config.notset")
}
idpConfigID, err := r.idGenerator.Next()
if err != nil {
return nil, err
}
//TODO: check name unique on aggregate
addedConfig := NewIAMIDPConfigWriteModel(config.AggregateID, idpConfigID)
clientSecret, err := crypto.Crypt([]byte(config.OIDCConfig.ClientSecretString), r.idpConfigSecretCrypto)
if err != nil {
return nil, err
}
iamAgg := IAMAggregateFromWriteModel(&addedConfig.WriteModel)
iamAgg.PushEvents(
iam_repo.NewIDPConfigAddedEvent(
ctx,
idpConfigID,
config.Name,
domain.IDPConfigType(config.Type),
domain.IDPConfigStylingType(config.StylingType),
),
)
iamAgg.PushEvents(
iam_repo.NewIDPOIDCConfigAddedEvent(
ctx, config.OIDCConfig.ClientID,
idpConfigID,
config.OIDCConfig.Issuer,
clientSecret,
domain.OIDCMappingField(config.OIDCConfig.IDPDisplayNameMapping),
domain.OIDCMappingField(config.OIDCConfig.UsernameMapping),
config.OIDCConfig.Scopes...,
),
)
err = r.eventstore.PushAggregate(ctx, addedConfig, iamAgg)
if err != nil {
return nil, err
}
return writeModelToIDPConfig(addedConfig), nil
}
func (r *CommandSide) ChangeDefaultIDPConfig(ctx context.Context, config *iam_model.IDPConfig) (*iam_model.IDPConfig, error) {
existingIDP, err := r.iamIDPConfigWriteModelByID(ctx, config.AggregateID, config.IDPConfigID)
if err != nil {
return nil, err
}
if existingIDP.State == domain.IDPConfigStateRemoved || existingIDP.State == domain.IDPConfigStateUnspecified {
return nil, caos_errs.ThrowAlreadyExists(nil, "IAM-4M9so", "Errors.IAM.IDPConfig.NotExisting")
}
changedEvent, hasChanged := existingIDP.NewChangedEvent(ctx, config.IDPConfigID, config.Name, domain.IDPConfigStylingType(config.StylingType))
if !hasChanged {
return nil, caos_errs.ThrowAlreadyExists(nil, "IAM-4M9vs", "Errors.IAM.LabelPolicy.NotChanged")
}
iamAgg := IAMAggregateFromWriteModel(&existingIDP.WriteModel)
iamAgg.PushEvents(changedEvent)
err = r.eventstore.PushAggregate(ctx, existingIDP, iamAgg)
if err != nil {
return nil, err
}
return writeModelToIDPConfig(existingIDP), nil
}
func (r *CommandSide) DeactivateDefaultIDPConfig(ctx context.Context, iamID, idpID string) (*iam_model.IDPConfig, error) {
existingIDP, err := r.iamIDPConfigWriteModelByID(ctx, iamID, idpID)
if err != nil {
return nil, err
}
if existingIDP.State != domain.IDPConfigStateActive {
return nil, caos_errs.ThrowAlreadyExists(nil, "IAM-4M9so", "Errors.IAM.IDPConfig.NotActive")
}
iamAgg := IAMAggregateFromWriteModel(&existingIDP.WriteModel)
iamAgg.PushEvents(iam_repo.NewIDPConfigDeactivatedEvent(ctx, idpID))
err = r.eventstore.PushAggregate(ctx, existingIDP, iamAgg)
if err != nil {
return nil, err
}
return writeModelToIDPConfig(existingIDP), nil
}
func (r *CommandSide) ReactivateDefaultIDPConfig(ctx context.Context, iamID, idpID string) (*iam_model.IDPConfig, error) {
existingIDP, err := r.iamIDPConfigWriteModelByID(ctx, iamID, idpID)
if err != nil {
return nil, err
}
if existingIDP.State != domain.IDPConfigStateInactive {
return nil, caos_errs.ThrowAlreadyExists(nil, "IAM-5Mo0d", "Errors.IAM.IDPConfig.NotInactive")
}
iamAgg := IAMAggregateFromWriteModel(&existingIDP.WriteModel)
iamAgg.PushEvents(iam_repo.NewIDPConfigReactivatedEvent(ctx, idpID))
err = r.eventstore.PushAggregate(ctx, existingIDP, iamAgg)
if err != nil {
return nil, err
}
return writeModelToIDPConfig(existingIDP), nil
}
func (r *CommandSide) RemoveDefaultIDPConfig(ctx context.Context, iamID, idpID string) (*iam_model.IDPConfig, error) {
writeModel, err := r.pushDefaultIDPWriteModel(ctx, iamID, idpID, func(a *iam.Aggregate, _ *IAMIDPConfigWriteModel) *iam.Aggregate {
a.Aggregate = *a.PushEvents(iam_repo.NewIDPConfigRemovedEvent(ctx, idpID))
return a
})
if err != nil {
return nil, err
}
return writeModelToIDPConfig(writeModel), nil
}
func (r *CommandSide) pushDefaultIDPWriteModel(ctx context.Context, iamID, idpID string, eventSetter func(*iam.Aggregate, *IAMIDPConfigWriteModel) *iam.Aggregate) (*IAMIDPConfigWriteModel, error) {
writeModel := NewIAMIDPConfigWriteModel(iamID, idpID)
err := r.eventstore.FilterToQueryReducer(ctx, writeModel)
if err != nil {
return nil, err
}
aggregate := eventSetter(IAMAggregateFromWriteModel(&writeModel.WriteModel), writeModel)
err = r.eventstore.PushAggregate(ctx, writeModel, aggregate)
if err != nil {
return nil, err
}
return writeModel, nil
}
func (r *CommandSide) iamIDPConfigWriteModelByID(ctx context.Context, iamID, idpID string) (policy *IAMIDPConfigWriteModel, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
writeModel := NewIAMIDPConfigWriteModel(iamID, idpID)
err = r.eventstore.FilterToQueryReducer(ctx, writeModel)
if err != nil {
return nil, err
}
return writeModel, nil
}
+100
View File
@@ -0,0 +1,100 @@
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/iam"
)
type IAMIDPConfigWriteModel struct {
IDPConfigWriteModel
}
func NewIAMIDPConfigWriteModel(iamID, configID string) *IAMIDPConfigWriteModel {
return &IAMIDPConfigWriteModel{
IDPConfigWriteModel{
WriteModel: eventstore.WriteModel{
AggregateID: iamID,
},
ConfigID: configID,
},
}
}
func (wm *IAMIDPConfigWriteModel) Query() *eventstore.SearchQueryBuilder {
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, iam.AggregateType).
AggregateIDs(wm.AggregateID)
}
func (wm *IAMIDPConfigWriteModel) AppendEvents(events ...eventstore.EventReader) {
for _, event := range events {
switch e := event.(type) {
case *iam.IDPConfigAddedEvent:
if wm.ConfigID != e.ConfigID {
continue
}
wm.IDPConfigWriteModel.AppendEvents(&e.IDPConfigAddedEvent)
case *iam.IDPConfigChangedEvent:
if wm.ConfigID != e.ConfigID {
continue
}
wm.IDPConfigWriteModel.AppendEvents(&e.IDPConfigChangedEvent)
case *iam.IDPConfigDeactivatedEvent:
if wm.ConfigID != e.ConfigID {
continue
}
wm.IDPConfigWriteModel.AppendEvents(&e.IDPConfigDeactivatedEvent)
case *iam.IDPConfigReactivatedEvent:
if wm.ConfigID != e.ConfigID {
continue
}
wm.IDPConfigWriteModel.AppendEvents(&e.IDPConfigReactivatedEvent)
case *iam.IDPConfigRemovedEvent:
if wm.ConfigID != e.ConfigID {
continue
}
wm.IDPConfigWriteModel.AppendEvents(&e.IDPConfigRemovedEvent)
case *iam.IDPOIDCConfigAddedEvent:
if wm.ConfigID != e.IDPConfigID {
continue
}
wm.IDPConfigWriteModel.AppendEvents(&e.OIDCConfigAddedEvent)
case *iam.IDPOIDCConfigChangedEvent:
if wm.ConfigID != e.IDPConfigID {
continue
}
wm.IDPConfigWriteModel.AppendEvents(&e.OIDCConfigChangedEvent)
}
}
}
func (wm *IAMIDPConfigWriteModel) Reduce() error {
return wm.IDPConfigWriteModel.Reduce()
}
func (wm *IAMIDPConfigWriteModel) AppendAndReduce(events ...eventstore.EventReader) error {
wm.AppendEvents(events...)
return wm.Reduce()
}
func (wm *IAMIDPConfigWriteModel) NewChangedEvent(
ctx context.Context,
configID,
name string,
stylingType domain.IDPConfigStylingType,
) (*iam.IDPConfigChangedEvent, bool) {
hasChanged := false
changedEvent := iam.NewIDPConfigChangedEvent(ctx)
changedEvent.ConfigID = configID
if wm.Name != name {
hasChanged = true
changedEvent.Name = name
}
if stylingType.Valid() && wm.StylingType != stylingType {
hasChanged = true
changedEvent.StylingType = stylingType
}
return changedEvent, hasChanged
}
@@ -0,0 +1,47 @@
package command
import (
"context"
caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/v2/domain"
iam_model "github.com/caos/zitadel/internal/iam/model"
)
func (r *CommandSide) ChangeDefaultIDPOIDCConfig(ctx context.Context, config *iam_model.OIDCIDPConfig) (*iam_model.OIDCIDPConfig, error) {
existingConfig := NewIDPOIDCConfigWriteModel(config.AggregateID, config.IDPConfigID)
err := r.eventstore.FilterToQueryReducer(ctx, existingConfig)
if err != nil {
return nil, err
}
if existingConfig.State == domain.IDPConfigStateRemoved || existingConfig.State == domain.IDPConfigStateUnspecified {
return nil, caos_errs.ThrowAlreadyExists(nil, "IAM-67J9d", "Errors.IAM.IDPConfig.AlreadyExists")
}
changedEvent, hasChanged, err := existingConfig.NewChangedEvent(
ctx,
config.ClientID,
config.Issuer,
config.ClientSecretString,
r.idpConfigSecretCrypto,
domain.OIDCMappingField(config.IDPDisplayNameMapping),
domain.OIDCMappingField(config.UsernameMapping),
config.Scopes...)
if err != nil {
return nil, err
}
if !hasChanged {
return nil, caos_errs.ThrowAlreadyExists(nil, "IAM-4M9vs", "Errors.IAM.LabelPolicy.NotChanged")
}
iamAgg := IAMAggregateFromWriteModel(&existingConfig.WriteModel)
iamAgg.PushEvents(changedEvent)
err = r.eventstore.PushAggregate(ctx, existingConfig, iamAgg)
if err != nil {
return nil, err
}
return writeModelToIDPOIDCConfig(&existingConfig.OIDCConfigWriteModel), nil
}
@@ -0,0 +1,115 @@
package command
import (
"context"
"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/iam"
"reflect"
)
type IDPOIDCConfigWriteModel struct {
OIDCConfigWriteModel
}
func NewIDPOIDCConfigWriteModel(iamID, idpConfigID string) *IDPOIDCConfigWriteModel {
return &IDPOIDCConfigWriteModel{
OIDCConfigWriteModel{
WriteModel: eventstore.WriteModel{
AggregateID: iamID,
},
IDPConfigID: idpConfigID,
},
}
}
func (wm *IDPOIDCConfigWriteModel) AppendEvents(events ...eventstore.EventReader) {
for _, event := range events {
switch e := event.(type) {
case *iam.IDPOIDCConfigAddedEvent:
if wm.IDPConfigID != e.IDPConfigID {
continue
}
wm.OIDCConfigWriteModel.AppendEvents(&e.OIDCConfigAddedEvent)
case *iam.IDPOIDCConfigChangedEvent:
if wm.IDPConfigID != e.IDPConfigID {
continue
}
wm.OIDCConfigWriteModel.AppendEvents(&e.OIDCConfigChangedEvent)
case *iam.IDPConfigReactivatedEvent:
if wm.IDPConfigID != e.ConfigID {
continue
}
wm.OIDCConfigWriteModel.AppendEvents(&e.IDPConfigReactivatedEvent)
case *iam.IDPConfigDeactivatedEvent:
if wm.IDPConfigID != e.ConfigID {
continue
}
wm.OIDCConfigWriteModel.AppendEvents(&e.IDPConfigDeactivatedEvent)
case *iam.IDPConfigRemovedEvent:
if wm.IDPConfigID != e.ConfigID {
continue
}
wm.OIDCConfigWriteModel.AppendEvents(&e.IDPConfigRemovedEvent)
default:
wm.OIDCConfigWriteModel.AppendEvents(e)
}
}
}
func (wm *IDPOIDCConfigWriteModel) Reduce() error {
if err := wm.OIDCConfigWriteModel.Reduce(); err != nil {
return err
}
return wm.WriteModel.Reduce()
}
func (wm *IDPOIDCConfigWriteModel) Query() *eventstore.SearchQueryBuilder {
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, iam.AggregateType).
AggregateIDs(wm.AggregateID)
}
func (wm *IDPOIDCConfigWriteModel) NewChangedEvent(
ctx context.Context,
clientID,
issuer,
clientSecretString string,
secretCrypto crypto.Crypto,
idpDisplayNameMapping,
userNameMapping domain.OIDCMappingField,
scopes ...string,
) (*iam.IDPOIDCConfigChangedEvent, bool, error) {
hasChanged := false
changedEvent := iam.NewIDPOIDCConfigChangedEvent(ctx)
var clientSecret *crypto.CryptoValue
var err error
if clientSecretString != "" {
clientSecret, err = crypto.Crypt([]byte(clientSecretString), secretCrypto)
if err != nil {
return nil, false, err
}
changedEvent.ClientSecret = clientSecret
}
if wm.ClientID != clientID {
hasChanged = true
changedEvent.ClientID = clientID
}
if wm.Issuer != issuer {
hasChanged = true
changedEvent.Issuer = issuer
}
if idpDisplayNameMapping.Valid() && wm.IDPDisplayNameMapping != idpDisplayNameMapping {
hasChanged = true
changedEvent.IDPDisplayNameMapping = idpDisplayNameMapping
}
if userNameMapping.Valid() && wm.UserNameMapping != userNameMapping {
hasChanged = true
changedEvent.UserNameMapping = userNameMapping
}
if reflect.DeepEqual(wm.Scopes, scopes) {
hasChanged = true
changedEvent.Scopes = scopes
}
return changedEvent, hasChanged, nil
}
+103
View File
@@ -0,0 +1,103 @@
package command
import (
"context"
"reflect"
"github.com/caos/zitadel/internal/errors"
caos_errs "github.com/caos/zitadel/internal/errors"
iam_model "github.com/caos/zitadel/internal/iam/model"
"github.com/caos/zitadel/internal/telemetry/tracing"
iam_repo "github.com/caos/zitadel/internal/v2/repository/iam"
)
func (r *CommandSide) AddIAMMember(ctx context.Context, member *iam_model.IAMMember) (*iam_model.IAMMember, error) {
//TODO: check if roles valid
if !member.IsValid() {
return nil, caos_errs.ThrowPreconditionFailed(nil, "IAM-W8m4l", "Errors.IAM.MemberInvalid")
}
addedMember := NewIAMMemberWriteModel(member.AggregateID, member.UserID)
err := r.eventstore.FilterToQueryReducer(ctx, addedMember)
if err != nil {
return nil, err
}
if addedMember.IsActive {
return nil, errors.ThrowAlreadyExists(nil, "IAM-PtXi1", "Errors.IAM.Member.AlreadyExists")
}
iamAgg := IAMAggregateFromWriteModel(&addedMember.MemberWriteModel.WriteModel)
iamAgg.PushEvents(iam_repo.NewMemberAddedEvent(ctx, member.UserID, member.Roles...))
err = r.eventstore.PushAggregate(ctx, addedMember, iamAgg)
if err != nil {
return nil, err
}
return writeModelToMember(addedMember), nil
}
//ChangeIAMMember updates an existing member
func (r *CommandSide) ChangeIAMMember(ctx context.Context, member *iam_model.IAMMember) (*iam_model.IAMMember, error) {
//TODO: check if roles valid
if !member.IsValid() {
return nil, caos_errs.ThrowPreconditionFailed(nil, "IAM-LiaZi", "Errors.IAM.MemberInvalid")
}
existingMember, err := r.iamMemberWriteModelByID(ctx, member.AggregateID, member.UserID)
if err != nil {
return nil, err
}
if reflect.DeepEqual(existingMember.Roles, member.Roles) {
return nil, caos_errs.ThrowPreconditionFailed(nil, "IAM-LiaZi", "Errors.IAM.Member.RolesNotChanged")
}
iamAgg := IAMAggregateFromWriteModel(&existingMember.MemberWriteModel.WriteModel)
iamAgg.PushEvents(iam_repo.NewMemberChangedEvent(ctx, member.UserID, member.Roles...))
events, err := r.eventstore.PushAggregates(ctx, iamAgg)
if err != nil {
return nil, err
}
existingMember.AppendEvents(events...)
if err = existingMember.Reduce(); err != nil {
return nil, err
}
return writeModelToMember(existingMember), nil
}
func (r *CommandSide) RemoveIAMMember(ctx context.Context, member *iam_model.IAMMember) error {
m, err := r.iamMemberWriteModelByID(ctx, member.AggregateID, member.UserID)
if err != nil && !errors.IsNotFound(err) {
return err
}
if errors.IsNotFound(err) {
return nil
}
iamAgg := IAMAggregateFromWriteModel(&m.MemberWriteModel.WriteModel)
iamAgg.PushEvents(iam_repo.NewMemberRemovedEvent(ctx, member.UserID))
return r.eventstore.PushAggregate(ctx, m, iamAgg)
}
func (r *CommandSide) iamMemberWriteModelByID(ctx context.Context, iamID, userID string) (member *IAMMemberWriteModel, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
writeModel := NewIAMMemberWriteModel(iamID, userID)
err = r.eventstore.FilterToQueryReducer(ctx, writeModel)
if err != nil {
return nil, err
}
if !writeModel.IsActive {
return nil, errors.ThrowNotFound(nil, "IAM-D8JxR", "Errors.NotFound")
}
return writeModel, nil
}
+52
View File
@@ -0,0 +1,52 @@
package command
import (
"github.com/caos/zitadel/internal/eventstore/v2"
"github.com/caos/zitadel/internal/v2/repository/iam"
)
type IAMMemberWriteModel struct {
MemberWriteModel
}
func NewIAMMemberWriteModel(iamID, userID string) *IAMMemberWriteModel {
return &IAMMemberWriteModel{
MemberWriteModel{
WriteModel: eventstore.WriteModel{
AggregateID: iamID,
},
UserID: userID,
},
}
}
func (wm *IAMMemberWriteModel) AppendEvents(events ...eventstore.EventReader) {
for _, event := range events {
switch e := event.(type) {
case *iam.MemberAddedEvent:
if e.UserID != wm.MemberWriteModel.UserID {
continue
}
wm.MemberWriteModel.AppendEvents(&e.MemberAddedEvent)
case *iam.MemberChangedEvent:
if e.UserID != wm.MemberWriteModel.UserID {
continue
}
wm.MemberWriteModel.AppendEvents(&e.MemberChangedEvent)
case *iam.MemberRemovedEvent:
if e.UserID != wm.MemberWriteModel.UserID {
continue
}
wm.MemberWriteModel.AppendEvents(&e.MemberRemovedEvent)
}
}
}
func (wm *IAMMemberWriteModel) Reduce() error {
return wm.MemberWriteModel.Reduce()
}
func (wm *IAMMemberWriteModel) Query() *eventstore.SearchQueryBuilder {
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, iam.AggregateType).
AggregateIDs(wm.MemberWriteModel.AggregateID)
}
+77
View File
@@ -0,0 +1,77 @@
package command
import (
"github.com/caos/zitadel/internal/eventstore/v2"
"github.com/caos/zitadel/internal/v2/domain"
"github.com/caos/zitadel/internal/v2/repository/iam"
)
type IAMWriteModel struct {
eventstore.WriteModel
SetUpStarted domain.Step
SetUpDone domain.Step
GlobalOrgID string
ProjectID string
}
func NewIAMriteModel(iamID string) *IAMWriteModel {
return &IAMWriteModel{
WriteModel: eventstore.WriteModel{
AggregateID: iamID,
},
}
}
func (wm *IAMWriteModel) AppendEvents(events ...eventstore.EventReader) {
wm.WriteModel.AppendEvents(events...)
//for _, event := range events {
// switch e := event.(type) {
// case *iam.LabelPolicyAddedEvent:
// wm.LabelPolicyWriteModel.AppendEvents(&e.LabelPolicyAddedEvent)
// case *iam.LabelPolicyChangedEvent:
// wm.LabelPolicyWriteModel.AppendEvents(&e.LabelPolicyChangedEvent)
// }
//}
}
func (wm *IAMWriteModel) Reduce() error {
for _, event := range wm.Events {
switch e := event.(type) {
case *iam.ProjectSetEvent:
wm.ProjectID = e.ProjectID
case *iam.GlobalOrgSetEvent:
wm.GlobalOrgID = e.OrgID
case *iam.SetupStepEvent:
if e.Done {
wm.SetUpDone = e.Step
} else {
wm.SetUpStarted = e.Step
}
}
}
return nil
}
func (wm *IAMWriteModel) Query() *eventstore.SearchQueryBuilder {
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, iam.AggregateType).
AggregateIDs(wm.AggregateID)
}
//
//func (wm *IAMLabelPolicyWriteModel) HasChanged(primaryColor, secondaryColor string) bool {
// if primaryColor != "" && wm.PrimaryColor != primaryColor {
// return true
// }
// if secondaryColor != "" && wm.SecondaryColor != secondaryColor {
// return true
// }
// return false
//}
func IAMAggregateFromWriteModel(wm *eventstore.WriteModel) *iam.Aggregate {
return &iam.Aggregate{
Aggregate: *eventstore.AggregateFromWriteModel(wm, iam.AggregateType, iam.AggregateVersion),
}
}
+76
View File
@@ -0,0 +1,76 @@
package command
import (
"context"
caos_errs "github.com/caos/zitadel/internal/errors"
iam_model "github.com/caos/zitadel/internal/iam/model"
"github.com/caos/zitadel/internal/telemetry/tracing"
iam_repo "github.com/caos/zitadel/internal/v2/repository/iam"
)
func (r *CommandSide) AddDefaultLabelPolicy(ctx context.Context, policy *iam_model.LabelPolicy) (*iam_model.LabelPolicy, error) {
if !policy.IsValid() {
return nil, caos_errs.ThrowPreconditionFailed(nil, "IAM-5Mv0s", "Errors.IAM.LabelPolicyInvalid")
}
addedPolicy := NewIAMLabelPolicyWriteModel(policy.AggregateID)
err := r.eventstore.FilterToQueryReducer(ctx, addedPolicy)
if err != nil {
return nil, err
}
if addedPolicy.IsActive {
return nil, caos_errs.ThrowAlreadyExists(nil, "IAM-2B0ps", "Errors.IAM.LabelPolicy.AlreadyExists")
}
iamAgg := IAMAggregateFromWriteModel(&addedPolicy.LabelPolicyWriteModel.WriteModel)
iamAgg.PushEvents(iam_repo.NewLabelPolicyAddedEvent(ctx, policy.PrimaryColor, policy.SecondaryColor))
err = r.eventstore.PushAggregate(ctx, addedPolicy, iamAgg)
if err != nil {
return nil, err
}
return writeModelToLabelPolicy(addedPolicy), nil
}
func (r *CommandSide) ChangeDefaultLabelPolicy(ctx context.Context, policy *iam_model.LabelPolicy) (*iam_model.LabelPolicy, error) {
if !policy.IsValid() {
return nil, caos_errs.ThrowPreconditionFailed(nil, "IAM-6M0od", "Errors.IAM.LabelPolicyInvalid")
}
existingPolicy, err := r.defaultLabelPolicyWriteModelByID(ctx, policy.AggregateID)
if err != nil {
return nil, err
}
if !existingPolicy.IsActive {
return nil, caos_errs.ThrowAlreadyExists(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")
}
iamAgg := IAMAggregateFromWriteModel(&existingPolicy.LabelPolicyWriteModel.WriteModel)
iamAgg.PushEvents(changedEvent)
err = r.eventstore.PushAggregate(ctx, existingPolicy, iamAgg)
if err != nil {
return nil, err
}
return writeModelToLabelPolicy(existingPolicy), nil
}
func (r *CommandSide) defaultLabelPolicyWriteModelByID(ctx context.Context, iamID string) (policy *IAMLabelPolicyWriteModel, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
writeModel := NewIAMLabelPolicyWriteModel(iamID)
err = r.eventstore.FilterToQueryReducer(ctx, writeModel)
if err != nil {
return nil, err
}
return writeModel, nil
}
@@ -0,0 +1,60 @@
package command
import (
"context"
"github.com/caos/zitadel/internal/eventstore/v2"
"github.com/caos/zitadel/internal/v2/repository/iam"
)
type IAMLabelPolicyWriteModel struct {
LabelPolicyWriteModel
}
func NewIAMLabelPolicyWriteModel(iamID string) *IAMLabelPolicyWriteModel {
return &IAMLabelPolicyWriteModel{
LabelPolicyWriteModel{
WriteModel: eventstore.WriteModel{
AggregateID: iamID,
},
},
}
}
func (wm *IAMLabelPolicyWriteModel) AppendEvents(events ...eventstore.EventReader) {
for _, event := range events {
switch e := event.(type) {
case *iam.LabelPolicyAddedEvent:
wm.LabelPolicyWriteModel.AppendEvents(&e.LabelPolicyAddedEvent)
case *iam.LabelPolicyChangedEvent:
wm.LabelPolicyWriteModel.AppendEvents(&e.LabelPolicyChangedEvent)
}
}
}
func (wm *IAMLabelPolicyWriteModel) Reduce() error {
return wm.LabelPolicyWriteModel.Reduce()
}
func (wm *IAMLabelPolicyWriteModel) Query() *eventstore.SearchQueryBuilder {
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, iam.AggregateType).
AggregateIDs(wm.LabelPolicyWriteModel.AggregateID)
}
func (wm *IAMLabelPolicyWriteModel) NewChangedEvent(
ctx context.Context,
primaryColor,
secondaryColor string,
) (*iam.LabelPolicyChangedEvent, bool) {
hasChanged := false
changedEvent := iam.NewLabelPolicyChangedEvent(ctx)
if wm.PrimaryColor != primaryColor {
hasChanged = true
changedEvent.PrimaryColor = primaryColor
}
if wm.SecondaryColor != secondaryColor {
hasChanged = true
changedEvent.SecondaryColor = secondaryColor
}
return changedEvent, hasChanged
}
+183
View File
@@ -0,0 +1,183 @@
package command
import (
"context"
caos_errs "github.com/caos/zitadel/internal/errors"
iam_model "github.com/caos/zitadel/internal/iam/model"
"github.com/caos/zitadel/internal/telemetry/tracing"
"github.com/caos/zitadel/internal/v2/domain"
iam_repo "github.com/caos/zitadel/internal/v2/repository/iam"
)
func (r *CommandSide) AddDefaultLoginPolicy(ctx context.Context, policy *iam_model.LoginPolicy) (*iam_model.LoginPolicy, error) {
addedPolicy := NewIAMLoginPolicyWriteModel(policy.AggregateID)
iamAgg, err := r.addDefaultLoginPolicy(ctx, addedPolicy, policy)
if err != nil {
return nil, err
}
err = r.eventstore.PushAggregate(ctx, addedPolicy, iamAgg)
if err != nil {
return nil, err
}
return writeModelToLoginPolicy(addedPolicy), nil
}
func (r *CommandSide) addDefaultLoginPolicy(ctx context.Context, addedPolicy *IAMLoginPolicyWriteModel, policy *iam_model.LoginPolicy) (*iam_repo.Aggregate, error) {
err := r.eventstore.FilterToQueryReducer(ctx, addedPolicy)
if err != nil {
return nil, err
}
if addedPolicy.IsActive {
return nil, caos_errs.ThrowAlreadyExists(nil, "IAM-2B0ps", "Errors.IAM.LoginPolicy.AlreadyExists")
}
iamAgg := IAMAggregateFromWriteModel(&addedPolicy.LoginPolicyWriteModel.WriteModel)
iamAgg.PushEvents(iam_repo.NewLoginPolicyAddedEvent(ctx, policy.AllowUsernamePassword, policy.AllowRegister, policy.AllowExternalIdp, policy.ForceMFA, domain.PasswordlessType(policy.PasswordlessType)))
return iamAgg, nil
}
func (r *CommandSide) ChangeDefaultLoginPolicy(ctx context.Context, policy *iam_model.LoginPolicy) (*iam_model.LoginPolicy, error) {
if !policy.IsValid() {
return nil, caos_errs.ThrowPreconditionFailed(nil, "IAM-6M0od", "Errors.IAM.LoginPolicyInvalid")
}
existingPolicy, err := r.defaultLoginPolicyWriteModelByID(ctx, policy.AggregateID)
if err != nil {
return nil, err
}
if !existingPolicy.IsActive {
return nil, caos_errs.ThrowAlreadyExists(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 nil, caos_errs.ThrowAlreadyExists(nil, "IAM-5M9vdd", "Errors.IAM.LoginPolicy.NotChanged")
}
iamAgg := IAMAggregateFromWriteModel(&existingPolicy.LoginPolicyWriteModel.WriteModel)
iamAgg.PushEvents(changedEvent)
err = r.eventstore.PushAggregate(ctx, existingPolicy, iamAgg)
if err != nil {
return nil, err
}
return writeModelToLoginPolicy(existingPolicy), nil
}
func (r *CommandSide) AddIDPProviderToDefaultLoginPolicy(ctx context.Context, idpProvider *iam_model.IDPProvider) (*iam_model.IDPProvider, error) {
idpModel := NewIAMIdentityProviderWriteModel(idpProvider.AggregateID, idpProvider.IDPConfigID)
err := r.eventstore.FilterToQueryReducer(ctx, idpModel)
if err != nil {
return nil, err
}
if idpModel.IsActive {
return nil, caos_errs.ThrowAlreadyExists(nil, "IAM-2B0ps", "Errors.IAM.LoginPolicy.IDP.AlreadyExists")
}
iamAgg := IAMAggregateFromWriteModel(&idpModel.WriteModel)
iamAgg.PushEvents(iam_repo.NewIdentityProviderAddedEvent(ctx, idpProvider.IDPConfigID, domain.IdentityProviderType(idpProvider.Type)))
if err = r.eventstore.PushAggregate(ctx, idpModel, iamAgg); err != nil {
return nil, err
}
return writeModelToIDPProvider(idpModel), nil
}
func (r *CommandSide) RemoveIDPProviderFromDefaultLoginPolicy(ctx context.Context, idpProvider *iam_model.IDPProvider) error {
idpModel := NewIAMIdentityProviderWriteModel(idpProvider.AggregateID, idpProvider.IDPConfigID)
err := r.eventstore.FilterToQueryReducer(ctx, idpModel)
if err != nil {
return err
}
if !idpModel.IsActive {
return caos_errs.ThrowAlreadyExists(nil, "IAM-39fjs", "Errors.IAM.LoginPolicy.IDP.NotExisting")
}
iamAgg := IAMAggregateFromWriteModel(&idpModel.IdentityProviderWriteModel.WriteModel)
iamAgg.PushEvents(iam_repo.NewIdentityProviderRemovedEvent(ctx, idpProvider.IDPConfigID))
return r.eventstore.PushAggregate(ctx, idpModel, iamAgg)
}
func (r *CommandSide) AddSecondFactorToDefaultLoginPolicy(ctx context.Context, iamID string, secondFactor iam_model.SecondFactorType) (iam_model.SecondFactorType, error) {
secondFactorModel := NewIAMSecondFactorWriteModel(iamID)
err := r.eventstore.FilterToQueryReducer(ctx, secondFactorModel)
if err != nil {
return iam_model.SecondFactorTypeUnspecified, err
}
if secondFactorModel.IsActive {
return iam_model.SecondFactorTypeUnspecified, caos_errs.ThrowAlreadyExists(nil, "IAM-2B0ps", "Errors.IAM.LoginPolicy.MFA.AlreadyExists")
}
iamAgg := IAMAggregateFromWriteModel(&secondFactorModel.SecondFactorWriteModel.WriteModel)
iamAgg.PushEvents(iam_repo.NewLoginPolicySecondFactorAddedEvent(ctx, domain.SecondFactorType(secondFactor)))
if err = r.eventstore.PushAggregate(ctx, secondFactorModel, iamAgg); err != nil {
return iam_model.SecondFactorTypeUnspecified, err
}
return iam_model.SecondFactorType(secondFactorModel.MFAType), nil
}
func (r *CommandSide) RemoveSecondFactorFromDefaultLoginPolicy(ctx context.Context, iamID string, secondFactor iam_model.SecondFactorType) error {
secondFactorModel := NewIAMSecondFactorWriteModel(iamID)
err := r.eventstore.FilterToQueryReducer(ctx, secondFactorModel)
if err != nil {
return err
}
if !secondFactorModel.IsActive {
return caos_errs.ThrowAlreadyExists(nil, "IAM-3M9od", "Errors.IAM.LoginPolicy.MFA.NotExisting")
}
iamAgg := IAMAggregateFromWriteModel(&secondFactorModel.SecondFactorWriteModel.WriteModel)
iamAgg.PushEvents(iam_repo.NewLoginPolicySecondFactorRemovedEvent(ctx, domain.SecondFactorType(secondFactor)))
return r.eventstore.PushAggregate(ctx, secondFactorModel, iamAgg)
}
func (r *CommandSide) AddMultiFactorToDefaultLoginPolicy(ctx context.Context, iamID string, multiFactor iam_model.MultiFactorType) (iam_model.MultiFactorType, error) {
multiFactorModel := NewIAMMultiFactorWriteModel(iamID)
err := r.eventstore.FilterToQueryReducer(ctx, multiFactorModel)
if err != nil {
return iam_model.MultiFactorTypeUnspecified, err
}
if multiFactorModel.IsActive {
return iam_model.MultiFactorTypeUnspecified, caos_errs.ThrowAlreadyExists(nil, "IAM-3M9od", "Errors.IAM.LoginPolicy.MFA.AlreadyExists")
}
iamAgg := IAMAggregateFromWriteModel(&multiFactorModel.MultiFactoryWriteModel.WriteModel)
iamAgg.PushEvents(iam_repo.NewLoginPolicyMultiFactorAddedEvent(ctx, domain.MultiFactorType(multiFactor)))
if err = r.eventstore.PushAggregate(ctx, multiFactorModel, iamAgg); err != nil {
return iam_model.MultiFactorTypeUnspecified, err
}
return iam_model.MultiFactorType(multiFactorModel.MultiFactoryWriteModel.MFAType), nil
}
func (r *CommandSide) RemoveMultiFactorFromDefaultLoginPolicy(ctx context.Context, iamID string, multiFactor iam_model.MultiFactorType) error {
multiFactorModel := NewIAMMultiFactorWriteModel(iamID)
err := r.eventstore.FilterToQueryReducer(ctx, multiFactorModel)
if err != nil {
return err
}
if multiFactorModel.IsActive {
return caos_errs.ThrowAlreadyExists(nil, "IAM-3M9df", "Errors.IAM.LoginPolicy.MFA.NotExisting")
}
iamAgg := IAMAggregateFromWriteModel(&multiFactorModel.MultiFactoryWriteModel.WriteModel)
iamAgg.PushEvents(iam_repo.NewLoginPolicyMultiFactorRemovedEvent(ctx, domain.MultiFactorType(multiFactor)))
return r.eventstore.PushAggregate(ctx, multiFactorModel, iamAgg)
}
func (r *CommandSide) defaultLoginPolicyWriteModelByID(ctx context.Context, iamID string) (policy *IAMLoginPolicyWriteModel, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
writeModel := NewIAMLoginPolicyWriteModel(iamID)
err = r.eventstore.FilterToQueryReducer(ctx, writeModel)
if err != nil {
return nil, err
}
return writeModel, nil
}
@@ -0,0 +1,70 @@
package command
import (
"github.com/caos/zitadel/internal/eventstore/v2"
"github.com/caos/zitadel/internal/v2/repository/iam"
)
type IAMSecondFactorWriteModel struct {
SecondFactorWriteModel
}
func NewIAMSecondFactorWriteModel(iamID string) *IAMSecondFactorWriteModel {
return &IAMSecondFactorWriteModel{
SecondFactorWriteModel{
WriteModel: eventstore.WriteModel{
AggregateID: iamID,
},
},
}
}
func (wm *IAMSecondFactorWriteModel) AppendEvents(events ...eventstore.EventReader) {
for _, event := range events {
switch e := event.(type) {
case *iam.LoginPolicySecondFactorAddedEvent:
wm.WriteModel.AppendEvents(&e.SecondFactorAddedEvent)
}
}
}
func (wm *IAMSecondFactorWriteModel) Reduce() error {
return wm.WriteModel.Reduce()
}
func (wm *IAMSecondFactorWriteModel) Query() *eventstore.SearchQueryBuilder {
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, iam.AggregateType).
AggregateIDs(wm.WriteModel.AggregateID)
}
type IAMMultiFactorWriteModel struct {
MultiFactoryWriteModel
}
func NewIAMMultiFactorWriteModel(iamID string) *IAMMultiFactorWriteModel {
return &IAMMultiFactorWriteModel{
MultiFactoryWriteModel{
WriteModel: eventstore.WriteModel{
AggregateID: iamID,
},
},
}
}
func (wm *IAMMultiFactorWriteModel) AppendEvents(events ...eventstore.EventReader) {
for _, event := range events {
switch e := event.(type) {
case *iam.LoginPolicyMultiFactorAddedEvent:
wm.WriteModel.AppendEvents(&e.MultiFactorAddedEvent)
}
}
}
func (wm *IAMMultiFactorWriteModel) Reduce() error {
return wm.WriteModel.Reduce()
}
func (wm *IAMMultiFactorWriteModel) Query() *eventstore.SearchQueryBuilder {
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, iam.AggregateType).
AggregateIDs(wm.WriteModel.AggregateID)
}
@@ -0,0 +1,42 @@
package command
import (
"github.com/caos/zitadel/internal/eventstore/v2"
"github.com/caos/zitadel/internal/v2/repository/iam"
)
type IAMIdentityProviderWriteModel struct {
IdentityProviderWriteModel
}
func NewIAMIdentityProviderWriteModel(iamID, idpConfigID string) *IAMIdentityProviderWriteModel {
return &IAMIdentityProviderWriteModel{
IdentityProviderWriteModel: IdentityProviderWriteModel{
WriteModel: eventstore.WriteModel{
AggregateID: iamID,
},
IDPConfigID: idpConfigID,
},
}
}
func (wm *IAMIdentityProviderWriteModel) AppendEvents(events ...eventstore.EventReader) {
for _, event := range events {
switch e := event.(type) {
case *iam.IdentityProviderAddedEvent:
if e.IDPConfigID != wm.IDPConfigID {
continue
}
wm.IdentityProviderWriteModel.AppendEvents(&e.IdentityProviderAddedEvent)
}
}
}
func (wm *IAMIdentityProviderWriteModel) Reduce() error {
return wm.IdentityProviderWriteModel.Reduce()
}
func (wm *IAMIdentityProviderWriteModel) Query() *eventstore.SearchQueryBuilder {
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, iam.AggregateType).
AggregateIDs(wm.AggregateID)
}
@@ -0,0 +1,80 @@
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/iam"
)
type IAMLoginPolicyWriteModel struct {
LoginPolicyWriteModel
}
func NewIAMLoginPolicyWriteModel(iamID string) *IAMLoginPolicyWriteModel {
return &IAMLoginPolicyWriteModel{
LoginPolicyWriteModel{
WriteModel: eventstore.WriteModel{
AggregateID: iamID,
},
},
}
}
func (wm *IAMLoginPolicyWriteModel) AppendEvents(events ...eventstore.EventReader) {
for _, event := range events {
switch e := event.(type) {
case *iam.LoginPolicyAddedEvent:
wm.LoginPolicyWriteModel.AppendEvents(&e.LoginPolicyAddedEvent)
case *iam.LoginPolicyChangedEvent:
wm.LoginPolicyWriteModel.AppendEvents(&e.LoginPolicyChangedEvent)
}
}
}
func (wm *IAMLoginPolicyWriteModel) IsValid() bool {
return wm.AggregateID != ""
}
func (wm *IAMLoginPolicyWriteModel) Reduce() error {
return wm.LoginPolicyWriteModel.Reduce()
}
func (wm *IAMLoginPolicyWriteModel) Query() *eventstore.SearchQueryBuilder {
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, iam.AggregateType).
AggregateIDs(wm.LoginPolicyWriteModel.AggregateID)
}
func (wm *IAMLoginPolicyWriteModel) NewChangedEvent(
ctx context.Context,
allowUsernamePassword,
allowRegister,
allowExternalIDP,
forceMFA bool,
passwordlessType domain.PasswordlessType,
) (*iam.LoginPolicyChangedEvent, bool) {
hasChanged := false
changedEvent := iam.NewLoginPolicyChangedEvent(ctx)
if wm.AllowUserNamePassword == allowUsernamePassword {
hasChanged = true
changedEvent.AllowUserNamePassword = allowUsernamePassword
}
if wm.AllowRegister == allowRegister {
hasChanged = true
changedEvent.AllowRegister = allowRegister
}
if wm.AllowExternalIDP == allowExternalIDP {
hasChanged = true
changedEvent.AllowExternalIDP = allowExternalIDP
}
if wm.ForceMFA != forceMFA {
hasChanged = true
changedEvent.ForceMFA = forceMFA
}
if passwordlessType.Valid() && wm.PasswordlessType != passwordlessType {
hasChanged = true
changedEvent.PasswordlessType = passwordlessType
}
return changedEvent, hasChanged
}
+77
View File
@@ -0,0 +1,77 @@
package command
import (
"context"
caos_errs "github.com/caos/zitadel/internal/errors"
iam_model "github.com/caos/zitadel/internal/iam/model"
"github.com/caos/zitadel/internal/telemetry/tracing"
iam_repo "github.com/caos/zitadel/internal/v2/repository/iam"
)
func (r *CommandSide) GetDefaultOrgIAMPolicy(ctx context.Context, aggregateID string) (*iam_model.OrgIAMPolicy, error) {
policyWriteModel := NewIAMOrgIAMPolicyWriteModel(aggregateID)
err := r.eventstore.FilterToQueryReducer(ctx, policyWriteModel)
if err != nil {
return nil, err
}
policy := writeModelToOrgIAMPolicy(policyWriteModel)
policy.Default = true
return policy, nil
}
func (r *CommandSide) AddDefaultOrgIAMPolicy(ctx context.Context, policy *iam_model.OrgIAMPolicy) (*iam_model.OrgIAMPolicy, error) {
addedPolicy := NewIAMOrgIAMPolicyWriteModel(policy.AggregateID)
err := r.eventstore.FilterToQueryReducer(ctx, addedPolicy)
if err != nil {
return nil, err
}
if addedPolicy.IsActive {
return nil, caos_errs.ThrowAlreadyExists(nil, "IAM-Lk0dS", "Errors.IAM.OrgIAMPolicy.AlreadyExists")
}
iamAgg := IAMAggregateFromWriteModel(&addedPolicy.PolicyOrgIAMWriteModel.WriteModel)
iamAgg.PushEvents(iam_repo.NewOrgIAMPolicyAddedEvent(ctx, policy.UserLoginMustBeDomain))
err = r.eventstore.PushAggregate(ctx, addedPolicy, iamAgg)
if err != nil {
return nil, err
}
return writeModelToOrgIAMPolicy(addedPolicy), nil
}
func (r *CommandSide) ChangeDefaultOrgIAMPolicy(ctx context.Context, policy *iam_model.OrgIAMPolicy) (*iam_model.OrgIAMPolicy, error) {
existingPolicy, err := r.defaultOrgIAMPolicyWriteModelByID(ctx, policy.AggregateID)
if err != nil {
return nil, err
}
if !existingPolicy.IsActive {
return nil, caos_errs.ThrowAlreadyExists(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")
}
iamAgg := IAMAggregateFromWriteModel(&existingPolicy.PolicyOrgIAMWriteModel.WriteModel)
iamAgg.PushEvents(changedEvent)
err = r.eventstore.PushAggregate(ctx, existingPolicy, iamAgg)
if err != nil {
return nil, err
}
return writeModelToOrgIAMPolicy(existingPolicy), nil
}
func (r *CommandSide) defaultOrgIAMPolicyWriteModelByID(ctx context.Context, iamID string) (policy *IAMOrgIAMPolicyWriteModel, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
writeModel := NewIAMOrgIAMPolicyWriteModel(iamID)
err = r.eventstore.FilterToQueryReducer(ctx, writeModel)
if err != nil {
return nil, err
}
return writeModel, nil
}
@@ -0,0 +1,51 @@
package command
import (
"context"
"github.com/caos/zitadel/internal/eventstore/v2"
"github.com/caos/zitadel/internal/v2/repository/iam"
)
type IAMOrgIAMPolicyWriteModel struct {
PolicyOrgIAMWriteModel
}
func NewIAMOrgIAMPolicyWriteModel(iamID string) *IAMOrgIAMPolicyWriteModel {
return &IAMOrgIAMPolicyWriteModel{
PolicyOrgIAMWriteModel{
WriteModel: eventstore.WriteModel{
AggregateID: iamID,
},
},
}
}
func (wm *IAMOrgIAMPolicyWriteModel) AppendEvents(events ...eventstore.EventReader) {
for _, event := range events {
switch e := event.(type) {
case *iam.OrgIAMPolicyAddedEvent:
wm.PolicyOrgIAMWriteModel.AppendEvents(&e.OrgIAMPolicyAddedEvent)
case *iam.OrgIAMPolicyChangedEvent:
wm.PolicyOrgIAMWriteModel.AppendEvents(&e.OrgIAMPolicyChangedEvent)
}
}
}
func (wm *IAMOrgIAMPolicyWriteModel) Reduce() error {
return wm.PolicyOrgIAMWriteModel.Reduce()
}
func (wm *IAMOrgIAMPolicyWriteModel) Query() *eventstore.SearchQueryBuilder {
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, iam.AggregateType).
AggregateIDs(wm.PolicyOrgIAMWriteModel.AggregateID)
}
func (wm *IAMOrgIAMPolicyWriteModel) NewChangedEvent(ctx context.Context, userLoginMustBeDomain bool) (*iam.OrgIAMPolicyChangedEvent, bool) {
hasChanged := false
changedEvent := iam.NewOrgIAMPolicyChangedEvent(ctx)
if wm.UserLoginMustBeDomain != userLoginMustBeDomain {
hasChanged = true
changedEvent.UserLoginMustBeDomain = userLoginMustBeDomain
}
return changedEvent, hasChanged
}
@@ -0,0 +1,67 @@
package command
import (
"context"
caos_errs "github.com/caos/zitadel/internal/errors"
iam_model "github.com/caos/zitadel/internal/iam/model"
"github.com/caos/zitadel/internal/telemetry/tracing"
iam_repo "github.com/caos/zitadel/internal/v2/repository/iam"
)
func (r *CommandSide) AddDefaultPasswordAgePolicy(ctx context.Context, policy *iam_model.PasswordAgePolicy) (*iam_model.PasswordAgePolicy, error) {
addedPolicy := NewIAMPasswordAgePolicyWriteModel(policy.AggregateID)
err := r.eventstore.FilterToQueryReducer(ctx, addedPolicy)
if err != nil {
return nil, err
}
if addedPolicy.IsActive {
return nil, caos_errs.ThrowAlreadyExists(nil, "IAM-2B0ps", "Errors.IAM.PasswordAgePolicy.AlreadyExists")
}
iamAgg := IAMAggregateFromWriteModel(&addedPolicy.PasswordAgePolicyWriteModel.WriteModel)
iamAgg.PushEvents(iam_repo.NewPasswordAgePolicyAddedEvent(ctx, policy.ExpireWarnDays, policy.MaxAgeDays))
err = r.eventstore.PushAggregate(ctx, addedPolicy, iamAgg)
if err != nil {
return nil, err
}
return writeModelToPasswordAgePolicy(addedPolicy), nil
}
func (r *CommandSide) ChangeDefaultPasswordAgePolicy(ctx context.Context, policy *iam_model.PasswordAgePolicy) (*iam_model.PasswordAgePolicy, error) {
existingPolicy, err := r.defaultPasswordAgePolicyWriteModelByID(ctx, policy.AggregateID)
if err != nil {
return nil, err
}
if !existingPolicy.IsActive {
return nil, caos_errs.ThrowAlreadyExists(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")
}
iamAgg := IAMAggregateFromWriteModel(&existingPolicy.PasswordAgePolicyWriteModel.WriteModel)
iamAgg.PushEvents(changedEvent)
err = r.eventstore.PushAggregate(ctx, existingPolicy, iamAgg)
if err != nil {
return nil, err
}
return writeModelToPasswordAgePolicy(existingPolicy), nil
}
func (r *CommandSide) defaultPasswordAgePolicyWriteModelByID(ctx context.Context, iamID string) (policy *IAMPasswordAgePolicyWriteModel, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
writeModel := NewIAMPasswordAgePolicyWriteModel(iamID)
err = r.eventstore.FilterToQueryReducer(ctx, writeModel)
if err != nil {
return nil, err
}
return writeModel, nil
}
@@ -0,0 +1,55 @@
package command
import (
"context"
"github.com/caos/zitadel/internal/eventstore/v2"
"github.com/caos/zitadel/internal/v2/repository/iam"
)
type IAMPasswordAgePolicyWriteModel struct {
PasswordAgePolicyWriteModel
}
func NewIAMPasswordAgePolicyWriteModel(iamID string) *IAMPasswordAgePolicyWriteModel {
return &IAMPasswordAgePolicyWriteModel{
PasswordAgePolicyWriteModel{
WriteModel: eventstore.WriteModel{
AggregateID: iamID,
},
},
}
}
func (wm *IAMPasswordAgePolicyWriteModel) AppendEvents(events ...eventstore.EventReader) {
for _, event := range events {
switch e := event.(type) {
case *iam.PasswordAgePolicyAddedEvent:
wm.PasswordAgePolicyWriteModel.AppendEvents(&e.PasswordAgePolicyAddedEvent)
case *iam.PasswordAgePolicyChangedEvent:
wm.PasswordAgePolicyWriteModel.AppendEvents(&e.PasswordAgePolicyChangedEvent)
}
}
}
func (wm *IAMPasswordAgePolicyWriteModel) Reduce() error {
return wm.PasswordAgePolicyWriteModel.Reduce()
}
func (wm *IAMPasswordAgePolicyWriteModel) Query() *eventstore.SearchQueryBuilder {
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, iam.AggregateType).
AggregateIDs(wm.PasswordAgePolicyWriteModel.AggregateID)
}
func (wm *IAMPasswordAgePolicyWriteModel) NewChangedEvent(ctx context.Context, expireWarnDays, maxAgeDays uint64) (*iam.PasswordAgePolicyChangedEvent, bool) {
hasChanged := false
changedEvent := iam.NewPasswordAgePolicyChangedEvent(ctx)
if wm.ExpireWarnDays != expireWarnDays {
hasChanged = true
changedEvent.ExpireWarnDays = expireWarnDays
}
if wm.MaxAgeDays != maxAgeDays {
hasChanged = true
changedEvent.MaxAgeDays = maxAgeDays
}
return changedEvent, hasChanged
}
@@ -0,0 +1,94 @@
package command
import (
"context"
caos_errs "github.com/caos/zitadel/internal/errors"
iam_model "github.com/caos/zitadel/internal/iam/model"
"github.com/caos/zitadel/internal/telemetry/tracing"
iam_repo "github.com/caos/zitadel/internal/v2/repository/iam"
)
func (r *CommandSide) GetDefaultPasswordComplexityPolicy(ctx context.Context, aggregateID string) (*iam_model.PasswordComplexityPolicy, error) {
policyWriteModel := NewIAMPasswordComplexityPolicyWriteModel(aggregateID)
err := r.eventstore.FilterToQueryReducer(ctx, policyWriteModel)
if err != nil {
return nil, err
}
policy := writeModelToPasswordComplexityPolicy(policyWriteModel)
policy.Default = true
return policy, nil
}
func (r *CommandSide) AddDefaultPasswordComplexityPolicy(ctx context.Context, policy *iam_model.PasswordComplexityPolicy) (*iam_model.PasswordComplexityPolicy, error) {
addedPolicy := NewIAMPasswordComplexityPolicyWriteModel(policy.AggregateID)
iamAgg, err := r.addDefaultPasswordComplexityPolicy(ctx, addedPolicy, policy)
if err != nil {
return nil, err
}
err = r.eventstore.PushAggregate(ctx, addedPolicy, iamAgg)
if err != nil {
return nil, err
}
return writeModelToPasswordComplexityPolicy(addedPolicy), nil
}
func (r *CommandSide) addDefaultPasswordComplexityPolicy(ctx context.Context, addedPolicy *IAMPasswordComplexityPolicyWriteModel, policy *iam_model.PasswordComplexityPolicy) (*iam_repo.Aggregate, error) {
if err := policy.IsValid(); err != nil {
return nil, err
}
err := r.eventstore.FilterToQueryReducer(ctx, addedPolicy)
if err != nil {
return nil, err
}
if addedPolicy.IsActive {
return nil, caos_errs.ThrowAlreadyExists(nil, "IAM-Lk0dS", "Errors.IAM.PasswordComplexityPolicy.AlreadyExists")
}
iamAgg := IAMAggregateFromWriteModel(&addedPolicy.PasswordComplexityPolicyWriteModel.WriteModel)
iamAgg.PushEvents(iam_repo.NewPasswordComplexityPolicyAddedEvent(ctx, policy.MinLength, policy.HasLowercase, policy.HasUppercase, policy.HasNumber, policy.HasSymbol))
return iamAgg, nil
}
func (r *CommandSide) ChangeDefaultPasswordComplexityPolicy(ctx context.Context, policy *iam_model.PasswordComplexityPolicy) (*iam_model.PasswordComplexityPolicy, error) {
if err := policy.IsValid(); err != nil {
return nil, err
}
existingPolicy, err := r.defaultPasswordComplexityPolicyWriteModelByID(ctx, policy.AggregateID)
if err != nil {
return nil, err
}
if !existingPolicy.IsActive {
return nil, caos_errs.ThrowAlreadyExists(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")
}
iamAgg := IAMAggregateFromWriteModel(&existingPolicy.PasswordComplexityPolicyWriteModel.WriteModel)
iamAgg.PushEvents(changedEvent)
err = r.eventstore.PushAggregate(ctx, existingPolicy, iamAgg)
if err != nil {
return nil, err
}
return writeModelToPasswordComplexityPolicy(existingPolicy), nil
}
func (r *CommandSide) defaultPasswordComplexityPolicyWriteModelByID(ctx context.Context, iamID string) (policy *IAMPasswordComplexityPolicyWriteModel, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
writeModel := NewIAMPasswordComplexityPolicyWriteModel(iamID)
err = r.eventstore.FilterToQueryReducer(ctx, writeModel)
if err != nil {
return nil, err
}
return writeModel, nil
}
@@ -0,0 +1,75 @@
package command
import (
"context"
"github.com/caos/zitadel/internal/eventstore/v2"
"github.com/caos/zitadel/internal/v2/repository/iam"
)
type IAMPasswordComplexityPolicyWriteModel struct {
PasswordComplexityPolicyWriteModel
}
func NewIAMPasswordComplexityPolicyWriteModel(iamID string) *IAMPasswordComplexityPolicyWriteModel {
return &IAMPasswordComplexityPolicyWriteModel{
PasswordComplexityPolicyWriteModel{
WriteModel: eventstore.WriteModel{
AggregateID: iamID,
},
},
}
}
func (wm *IAMPasswordComplexityPolicyWriteModel) AppendEvents(events ...eventstore.EventReader) {
for _, event := range events {
switch e := event.(type) {
case *iam.PasswordComplexityPolicyAddedEvent:
wm.PasswordComplexityPolicyWriteModel.AppendEvents(&e.PasswordComplexityPolicyAddedEvent)
case *iam.PasswordComplexityPolicyChangedEvent:
wm.PasswordComplexityPolicyWriteModel.AppendEvents(&e.PasswordComplexityPolicyChangedEvent)
}
}
}
func (wm *IAMPasswordComplexityPolicyWriteModel) Reduce() error {
return wm.PasswordComplexityPolicyWriteModel.Reduce()
}
func (wm *IAMPasswordComplexityPolicyWriteModel) Query() *eventstore.SearchQueryBuilder {
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, iam.AggregateType).
AggregateIDs(wm.PasswordComplexityPolicyWriteModel.AggregateID)
}
func (wm *IAMPasswordComplexityPolicyWriteModel) NewChangedEvent(
ctx context.Context,
minLength uint64,
hasLowercase,
hasUppercase,
hasNumber,
hasSymbol bool,
) (*iam.PasswordComplexityPolicyChangedEvent, bool) {
hasChanged := false
changedEvent := iam.NewPasswordComplexityPolicyChangedEvent(ctx)
if wm.MinLength != minLength {
hasChanged = true
changedEvent.MinLength = minLength
}
if wm.HasLowercase != hasLowercase {
hasChanged = true
changedEvent.HasLowercase = hasLowercase
}
if wm.HasUpperCase != hasUppercase {
hasChanged = true
changedEvent.HasUpperCase = hasUppercase
}
if wm.HasNumber != hasNumber {
hasChanged = true
changedEvent.HasNumber = hasNumber
}
if wm.HasSymbol != hasSymbol {
hasChanged = true
changedEvent.HasSymbol = hasSymbol
}
return changedEvent, hasChanged
}
@@ -0,0 +1,67 @@
package command
import (
"context"
caos_errs "github.com/caos/zitadel/internal/errors"
iam_model "github.com/caos/zitadel/internal/iam/model"
"github.com/caos/zitadel/internal/telemetry/tracing"
iam_repo "github.com/caos/zitadel/internal/v2/repository/iam"
)
func (r *CommandSide) AddDefaultPasswordLockoutPolicy(ctx context.Context, policy *iam_model.PasswordLockoutPolicy) (*iam_model.PasswordLockoutPolicy, error) {
addedPolicy := NewIAMPasswordLockoutPolicyWriteModel(policy.AggregateID)
err := r.eventstore.FilterToQueryReducer(ctx, addedPolicy)
if err != nil {
return nil, err
}
if addedPolicy.IsActive {
return nil, caos_errs.ThrowAlreadyExists(nil, "IAM-0olDf", "Errors.IAM.PasswordLockoutPolicy.AlreadyExists")
}
iamAgg := IAMAggregateFromWriteModel(&addedPolicy.PasswordLockoutPolicyWriteModel.WriteModel)
iamAgg.PushEvents(iam_repo.NewPasswordLockoutPolicyAddedEvent(ctx, policy.MaxAttempts, policy.ShowLockOutFailures))
err = r.eventstore.PushAggregate(ctx, addedPolicy, iamAgg)
if err != nil {
return nil, err
}
return writeModelToPasswordLockoutPolicy(addedPolicy), nil
}
func (r *CommandSide) ChangeDefaultPasswordLockoutPolicy(ctx context.Context, policy *iam_model.PasswordLockoutPolicy) (*iam_model.PasswordLockoutPolicy, error) {
existingPolicy, err := r.defaultPasswordLockoutPolicyWriteModelByID(ctx, policy.AggregateID)
if err != nil {
return nil, err
}
if !existingPolicy.IsActive {
return nil, caos_errs.ThrowAlreadyExists(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")
}
iamAgg := IAMAggregateFromWriteModel(&existingPolicy.PasswordLockoutPolicyWriteModel.WriteModel)
iamAgg.PushEvents(changedEvent)
err = r.eventstore.PushAggregate(ctx, existingPolicy, iamAgg)
if err != nil {
return nil, err
}
return writeModelToPasswordLockoutPolicy(existingPolicy), nil
}
func (r *CommandSide) defaultPasswordLockoutPolicyWriteModelByID(ctx context.Context, iamID string) (policy *IAMPasswordLockoutPolicyWriteModel, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
writeModel := NewIAMPasswordLockoutPolicyWriteModel(iamID)
err = r.eventstore.FilterToQueryReducer(ctx, writeModel)
if err != nil {
return nil, err
}
return writeModel, nil
}
@@ -0,0 +1,55 @@
package command
import (
"context"
"github.com/caos/zitadel/internal/eventstore/v2"
"github.com/caos/zitadel/internal/v2/repository/iam"
)
type IAMPasswordLockoutPolicyWriteModel struct {
PasswordLockoutPolicyWriteModel
}
func NewIAMPasswordLockoutPolicyWriteModel(iamID string) *IAMPasswordLockoutPolicyWriteModel {
return &IAMPasswordLockoutPolicyWriteModel{
PasswordLockoutPolicyWriteModel{
WriteModel: eventstore.WriteModel{
AggregateID: iamID,
},
},
}
}
func (wm *IAMPasswordLockoutPolicyWriteModel) AppendEvents(events ...eventstore.EventReader) {
for _, event := range events {
switch e := event.(type) {
case *iam.PasswordLockoutPolicyAddedEvent:
wm.PasswordLockoutPolicyWriteModel.AppendEvents(&e.PasswordLockoutPolicyAddedEvent)
case *iam.PasswordLockoutPolicyChangedEvent:
wm.PasswordLockoutPolicyWriteModel.AppendEvents(&e.PasswordLockoutPolicyChangedEvent)
}
}
}
func (wm *IAMPasswordLockoutPolicyWriteModel) Reduce() error {
return wm.PasswordLockoutPolicyWriteModel.Reduce()
}
func (wm *IAMPasswordLockoutPolicyWriteModel) Query() *eventstore.SearchQueryBuilder {
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, iam.AggregateType).
AggregateIDs(wm.PasswordLockoutPolicyWriteModel.AggregateID)
}
func (wm *IAMPasswordLockoutPolicyWriteModel) NewChangedEvent(ctx context.Context, maxAttempts uint64, showLockoutFailure bool) (*iam.PasswordLockoutPolicyChangedEvent, bool) {
hasChanged := false
changedEvent := iam.NewPasswordLockoutPolicyChangedEvent(ctx)
if wm.MaxAttempts != maxAttempts {
hasChanged = true
changedEvent.MaxAttempts = maxAttempts
}
if wm.ShowLockOutFailures != showLockoutFailure {
hasChanged = true
changedEvent.ShowLockOutFailures = showLockoutFailure
}
return changedEvent, hasChanged
}
@@ -0,0 +1,29 @@
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"
)
type IdentityProviderWriteModel struct {
eventstore.WriteModel
IDPConfigID string
IDPProviderType domain.IdentityProviderType
IsActive bool
}
func (wm *IdentityProviderWriteModel) Reduce() error {
for _, event := range wm.Events {
switch e := event.(type) {
case *policy.IdentityProviderAddedEvent:
wm.IDPConfigID = e.IDPConfigID
wm.IDPProviderType = e.IDPProviderType
wm.IsActive = true
case *policy.IdentityProviderRemovedEvent:
wm.IsActive = false
}
}
return wm.WriteModel.Reduce()
}
+75
View File
@@ -0,0 +1,75 @@
package command
import (
"github.com/caos/zitadel/internal/eventstore/v2"
"github.com/caos/zitadel/internal/v2/domain"
"github.com/caos/zitadel/internal/v2/repository/idpconfig"
)
type IDPConfigWriteModel struct {
eventstore.WriteModel
State domain.IDPConfigState
ConfigID string
Name string
StylingType domain.IDPConfigStylingType
OIDCConfig *OIDCConfigWriteModel
}
func (rm *IDPConfigWriteModel) AppendEvents(events ...eventstore.EventReader) {
rm.WriteModel.AppendEvents(events...)
for _, event := range events {
switch event.(type) {
case *idpconfig.OIDCConfigAddedEvent:
rm.OIDCConfig = new(OIDCConfigWriteModel)
rm.OIDCConfig.AppendEvents(event)
case *idpconfig.OIDCConfigChangedEvent:
rm.OIDCConfig.AppendEvents(event)
}
}
}
func (rm *IDPConfigWriteModel) Reduce() error {
for _, event := range rm.Events {
switch e := event.(type) {
case *idpconfig.IDPConfigAddedEvent:
rm.reduceConfigAddedEvent(e)
case *idpconfig.IDPConfigChangedEvent:
rm.reduceConfigChangedEvent(e)
case *idpconfig.IDPConfigDeactivatedEvent:
rm.reduceConfigStateChanged(e.ConfigID, domain.IDPConfigStateInactive)
case *idpconfig.IDPConfigReactivatedEvent:
rm.reduceConfigStateChanged(e.ConfigID, domain.IDPConfigStateActive)
case *idpconfig.IDPConfigRemovedEvent:
rm.reduceConfigStateChanged(e.ConfigID, domain.IDPConfigStateRemoved)
}
}
if rm.OIDCConfig != nil {
if err := rm.OIDCConfig.Reduce(); err != nil {
return err
}
}
return rm.WriteModel.Reduce()
}
func (rm *IDPConfigWriteModel) reduceConfigAddedEvent(e *idpconfig.IDPConfigAddedEvent) {
rm.ConfigID = e.ConfigID
rm.Name = e.Name
rm.StylingType = e.StylingType
rm.State = domain.IDPConfigStateActive
}
func (rm *IDPConfigWriteModel) reduceConfigChangedEvent(e *idpconfig.IDPConfigChangedEvent) {
if e.Name != "" {
rm.Name = e.Name
}
if e.StylingType.Valid() {
rm.StylingType = e.StylingType
}
}
func (rm *IDPConfigWriteModel) reduceConfigStateChanged(configID string, state domain.IDPConfigState) {
rm.State = state
}
+37
View File
@@ -0,0 +1,37 @@
package command
import (
"github.com/caos/zitadel/internal/eventstore/v2"
"github.com/caos/zitadel/internal/v2/repository/member"
)
type MemberWriteModel struct {
eventstore.WriteModel
UserID string
Roles []string
IsActive bool
}
func NewMemberWriteModel(userID string) *MemberWriteModel {
return &MemberWriteModel{
UserID: userID,
}
}
func (wm *MemberWriteModel) Reduce() error {
for _, event := range wm.Events {
switch e := event.(type) {
case *member.MemberAddedEvent:
wm.UserID = e.UserID
wm.Roles = e.Roles
wm.IsActive = true
case *member.MemberChangedEvent:
wm.Roles = e.Roles
case *member.MemberRemovedEvent:
wm.Roles = nil
wm.IsActive = false
}
}
return wm.WriteModel.Reduce()
}
+70
View 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/idpconfig"
)
type OIDCConfigWriteModel struct {
eventstore.WriteModel
IDPConfigID string
ClientID string
ClientSecret *crypto.CryptoValue
Issuer string
Scopes []string
IDPDisplayNameMapping domain.OIDCMappingField
UserNameMapping domain.OIDCMappingField
State domain.IDPConfigState
}
func (wm *OIDCConfigWriteModel) Reduce() error {
for _, event := range wm.Events {
switch e := event.(type) {
case *idpconfig.OIDCConfigAddedEvent:
wm.reduceConfigAddedEvent(e)
case *idpconfig.OIDCConfigChangedEvent:
wm.reduceConfigChangedEvent(e)
case *idpconfig.IDPConfigDeactivatedEvent:
wm.State = domain.IDPConfigStateInactive
case *idpconfig.IDPConfigReactivatedEvent:
wm.State = domain.IDPConfigStateActive
case *idpconfig.IDPConfigRemovedEvent:
wm.State = domain.IDPConfigStateRemoved
}
}
return wm.WriteModel.Reduce()
}
func (wm *OIDCConfigWriteModel) reduceConfigAddedEvent(e *idpconfig.OIDCConfigAddedEvent) {
wm.IDPConfigID = e.IDPConfigID
wm.ClientID = e.ClientID
wm.ClientSecret = e.ClientSecret
wm.Issuer = e.Issuer
wm.Scopes = e.Scopes
wm.IDPDisplayNameMapping = e.IDPDisplayNameMapping
wm.UserNameMapping = e.UserNameMapping
wm.State = domain.IDPConfigStateActive
}
func (wm *OIDCConfigWriteModel) reduceConfigChangedEvent(e *idpconfig.OIDCConfigChangedEvent) {
if e.ClientID != "" {
wm.ClientID = e.ClientID
}
if e.Issuer != "" {
wm.Issuer = e.Issuer
}
if len(e.Scopes) > 0 {
wm.Scopes = e.Scopes
}
if e.IDPDisplayNameMapping.Valid() {
wm.IDPDisplayNameMapping = e.IDPDisplayNameMapping
}
if e.UserNameMapping.Valid() {
wm.UserNameMapping = e.UserNameMapping
}
}
+23
View File
@@ -0,0 +1,23 @@
package command
import (
"github.com/caos/zitadel/internal/iam/model"
)
func orgWriteModelToOrgIAMPolicy(wm *ORGOrgIAMPolicyWriteModel) *model.OrgIAMPolicy {
return &model.OrgIAMPolicy{
ObjectRoot: writeModelToObjectRoot(wm.PolicyOrgIAMWriteModel.WriteModel),
UserLoginMustBeDomain: wm.UserLoginMustBeDomain,
}
}
func orgWriteModelToPasswordComplexityPolicy(wm *OrgPasswordComplexityPolicyWriteModel) *model.PasswordComplexityPolicy {
return &model.PasswordComplexityPolicy{
ObjectRoot: writeModelToObjectRoot(wm.PasswordComplexityPolicyWriteModel.WriteModel),
MinLength: wm.MinLength,
HasLowercase: wm.HasLowercase,
HasUppercase: wm.HasUpperCase,
HasNumber: wm.HasNumber,
HasSymbol: wm.HasSymbol,
}
}
+5
View File
@@ -0,0 +1,5 @@
package command
type OrgMemberWriteModel struct {
MemberWriteModel
}
+12
View File
@@ -0,0 +1,12 @@
package command
import (
"github.com/caos/zitadel/internal/eventstore/v2"
"github.com/caos/zitadel/internal/v2/repository/org"
)
func ORGAggregateFromWriteModel(wm *eventstore.WriteModel) *org.Aggregate {
return &org.Aggregate{
Aggregate: *eventstore.AggregateFromWriteModel(wm, org.AggregateType, org.AggregateVersion),
}
}
+78
View File
@@ -0,0 +1,78 @@
package command
import (
"context"
caos_errs "github.com/caos/zitadel/internal/errors"
iam_model "github.com/caos/zitadel/internal/iam/model"
"github.com/caos/zitadel/internal/telemetry/tracing"
iam_repo "github.com/caos/zitadel/internal/v2/repository/iam"
)
func (r *CommandSide) GetOrgIAMPolicy(ctx context.Context, orgID string) (*iam_model.OrgIAMPolicy, error) {
policy := NewORGOrgIAMPolicyWriteModel(orgID)
err := r.eventstore.FilterToQueryReducer(ctx, policy)
if err != nil {
return nil, err
}
if policy.IsActive {
return orgWriteModelToOrgIAMPolicy(policy), nil
}
return r.GetDefaultOrgIAMPolicy(ctx, r.iamID)
}
func (r *CommandSide) AddOrgIAMPolicy(ctx context.Context, policy *iam_model.OrgIAMPolicy) (*iam_model.OrgIAMPolicy, error) {
addedPolicy := NewORGOrgIAMPolicyWriteModel(policy.AggregateID)
err := r.eventstore.FilterToQueryReducer(ctx, addedPolicy)
if err != nil {
return nil, err
}
if addedPolicy.IsActive {
return nil, caos_errs.ThrowAlreadyExists(nil, "ORG-5M0ds", "Errors.Org.OrgIAMPolicy.AlreadyExists")
}
orgAgg := ORGAggregateFromWriteModel(&addedPolicy.PolicyOrgIAMWriteModel.WriteModel)
orgAgg.PushEvents(iam_repo.NewOrgIAMPolicyAddedEvent(ctx, policy.UserLoginMustBeDomain))
err = r.eventstore.PushAggregate(ctx, addedPolicy, orgAgg)
if err != nil {
return nil, err
}
return orgWriteModelToOrgIAMPolicy(addedPolicy), nil
}
func (r *CommandSide) ChangeOrgIAMPolicy(ctx context.Context, policy *iam_model.OrgIAMPolicy) (*iam_model.OrgIAMPolicy, error) {
existingPolicy, err := r.orgIAMPolicyWriteModelByID(ctx, policy.AggregateID)
if err != nil {
return nil, err
}
if !existingPolicy.IsActive {
return nil, caos_errs.ThrowAlreadyExists(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")
}
orgAgg := ORGAggregateFromWriteModel(&existingPolicy.PolicyOrgIAMWriteModel.WriteModel)
orgAgg.PushEvents(changedEvent)
err = r.eventstore.PushAggregate(ctx, existingPolicy, orgAgg)
if err != nil {
return nil, err
}
return orgWriteModelToOrgIAMPolicy(existingPolicy), nil
}
func (r *CommandSide) orgIAMPolicyWriteModelByID(ctx context.Context, iamID string) (policy *ORGOrgIAMPolicyWriteModel, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
writeModel := NewORGOrgIAMPolicyWriteModel(iamID)
err = r.eventstore.FilterToQueryReducer(ctx, writeModel)
if err != nil {
return nil, err
}
return writeModel, nil
}
@@ -0,0 +1,51 @@
package command
import (
"context"
"github.com/caos/zitadel/internal/eventstore/v2"
"github.com/caos/zitadel/internal/v2/repository/org"
)
type ORGOrgIAMPolicyWriteModel struct {
PolicyOrgIAMWriteModel
}
func NewORGOrgIAMPolicyWriteModel(orgID string) *ORGOrgIAMPolicyWriteModel {
return &ORGOrgIAMPolicyWriteModel{
PolicyOrgIAMWriteModel{
WriteModel: eventstore.WriteModel{
AggregateID: orgID,
},
},
}
}
func (wm *ORGOrgIAMPolicyWriteModel) AppendEvents(events ...eventstore.EventReader) {
for _, event := range events {
switch e := event.(type) {
case *org.OrgIAMPolicyAddedEvent:
wm.PolicyOrgIAMWriteModel.AppendEvents(&e.OrgIAMPolicyAddedEvent)
case *org.OrgIAMPolicyChangedEvent:
wm.PolicyOrgIAMWriteModel.AppendEvents(&e.OrgIAMPolicyChangedEvent)
}
}
}
func (wm *ORGOrgIAMPolicyWriteModel) Reduce() error {
return wm.PolicyOrgIAMWriteModel.Reduce()
}
func (wm *ORGOrgIAMPolicyWriteModel) Query() *eventstore.SearchQueryBuilder {
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, org.AggregateType).
AggregateIDs(wm.PolicyOrgIAMWriteModel.AggregateID)
}
func (wm *ORGOrgIAMPolicyWriteModel) NewChangedEvent(ctx context.Context, userLoginMustBeDomain bool) (*org.OrgIAMPolicyChangedEvent, bool) {
hasChanged := false
changedEvent := org.NewOrgIAMPolicyChangedEvent(ctx)
if wm.UserLoginMustBeDomain != userLoginMustBeDomain {
hasChanged = true
changedEvent.UserLoginMustBeDomain = userLoginMustBeDomain
}
return changedEvent, hasChanged
}
@@ -0,0 +1,18 @@
package command
import (
"context"
iam_model "github.com/caos/zitadel/internal/iam/model"
)
func (r *CommandSide) GetOrgPasswordComplexityPolicy(ctx context.Context, orgID string) (*iam_model.PasswordComplexityPolicy, error) {
policy := NewOrgPasswordComplexityPolicyWriteModel(orgID)
err := r.eventstore.FilterToQueryReducer(ctx, policy)
if err != nil {
return nil, err
}
if policy.IsActive {
return orgWriteModelToPasswordComplexityPolicy(policy), nil
}
return r.GetDefaultPasswordComplexityPolicy(ctx, r.iamID)
}
@@ -0,0 +1,75 @@
package command
import (
"context"
"github.com/caos/zitadel/internal/eventstore/v2"
"github.com/caos/zitadel/internal/v2/repository/org"
)
type OrgPasswordComplexityPolicyWriteModel struct {
PasswordComplexityPolicyWriteModel
}
func NewOrgPasswordComplexityPolicyWriteModel(iamID string) *OrgPasswordComplexityPolicyWriteModel {
return &OrgPasswordComplexityPolicyWriteModel{
PasswordComplexityPolicyWriteModel{
WriteModel: eventstore.WriteModel{
AggregateID: iamID,
},
},
}
}
func (wm *OrgPasswordComplexityPolicyWriteModel) AppendEvents(events ...eventstore.EventReader) {
for _, event := range events {
switch e := event.(type) {
case *org.PasswordComplexityPolicyAddedEvent:
wm.PasswordComplexityPolicyWriteModel.AppendEvents(&e.PasswordComplexityPolicyAddedEvent)
case *org.PasswordComplexityPolicyChangedEvent:
wm.PasswordComplexityPolicyWriteModel.AppendEvents(&e.PasswordComplexityPolicyChangedEvent)
}
}
}
func (wm *OrgPasswordComplexityPolicyWriteModel) Reduce() error {
return wm.PasswordComplexityPolicyWriteModel.Reduce()
}
func (wm *OrgPasswordComplexityPolicyWriteModel) Query() *eventstore.SearchQueryBuilder {
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, org.AggregateType).
AggregateIDs(wm.PasswordComplexityPolicyWriteModel.AggregateID)
}
func (wm *OrgPasswordComplexityPolicyWriteModel) NewChangedEvent(
ctx context.Context,
minLength uint64,
hasLowercase,
hasUppercase,
hasNumber,
hasSymbol bool,
) (*org.PasswordComplexityPolicyChangedEvent, bool) {
hasChanged := false
changedEvent := org.NewPasswordComplexityPolicyChangedEvent(ctx)
if wm.MinLength != minLength {
hasChanged = true
changedEvent.MinLength = minLength
}
if wm.HasLowercase != hasLowercase {
hasChanged = true
changedEvent.HasLowercase = hasLowercase
}
if wm.HasUpperCase != hasUppercase {
hasChanged = true
changedEvent.HasUpperCase = hasUppercase
}
if wm.HasNumber != hasNumber {
hasChanged = true
changedEvent.HasNumber = hasNumber
}
if wm.HasSymbol != hasSymbol {
hasChanged = true
changedEvent.HasSymbol = hasSymbol
}
return changedEvent, hasChanged
}
+31
View File
@@ -0,0 +1,31 @@
package command
import (
"github.com/caos/zitadel/internal/eventstore/v2"
"github.com/caos/zitadel/internal/v2/repository/policy"
)
type LabelPolicyWriteModel struct {
eventstore.WriteModel
PrimaryColor string
SecondaryColor string
IsActive bool
}
func (wm *LabelPolicyWriteModel) Reduce() error {
for _, event := range wm.Events {
switch e := event.(type) {
case *policy.LabelPolicyAddedEvent:
wm.PrimaryColor = e.PrimaryColor
wm.SecondaryColor = e.SecondaryColor
wm.IsActive = true
case *policy.LabelPolicyChangedEvent:
wm.PrimaryColor = e.PrimaryColor
wm.SecondaryColor = e.SecondaryColor
case *policy.LabelPolicyRemovedEvent:
wm.IsActive = false
}
}
return wm.WriteModel.Reduce()
}
@@ -0,0 +1,47 @@
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"
)
type SecondFactorWriteModel struct {
eventstore.WriteModel
MFAType domain.SecondFactorType
IsActive bool
}
func (wm *SecondFactorWriteModel) Reduce() error {
for _, event := range wm.Events {
switch e := event.(type) {
case *policy.SecondFactorAddedEvent:
wm.MFAType = e.MFAType
wm.IsActive = true
case *policy.SecondFactorRemovedEvent:
wm.MFAType = e.MFAType
wm.IsActive = false
}
}
return wm.WriteModel.Reduce()
}
type MultiFactoryWriteModel struct {
eventstore.WriteModel
MFAType domain.MultiFactorType
IsActive bool
}
func (wm *MultiFactoryWriteModel) Reduce() error {
for _, event := range wm.Events {
switch e := event.(type) {
case *policy.MultiFactorAddedEvent:
wm.MFAType = e.MFAType
wm.IsActive = true
case *policy.MultiFactorRemovedEvent:
wm.MFAType = e.MFAType
wm.IsActive = false
}
}
return wm.WriteModel.Reduce()
}
+41
View File
@@ -0,0 +1,41 @@
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"
)
type LoginPolicyWriteModel struct {
eventstore.WriteModel
AllowUserNamePassword bool
AllowRegister bool
AllowExternalIDP bool
ForceMFA bool
PasswordlessType domain.PasswordlessType
IsActive bool
}
func (wm *LoginPolicyWriteModel) Reduce() error {
for _, event := range wm.Events {
switch e := event.(type) {
case *policy.LoginPolicyAddedEvent:
wm.AllowRegister = e.AllowRegister
wm.AllowUserNamePassword = e.AllowUserNamePassword
wm.AllowExternalIDP = e.AllowExternalIDP
wm.ForceMFA = e.ForceMFA
wm.PasswordlessType = e.PasswordlessType
wm.IsActive = true
case *policy.LoginPolicyChangedEvent:
wm.AllowRegister = e.AllowRegister
wm.AllowUserNamePassword = e.AllowUserNamePassword
wm.AllowExternalIDP = e.AllowExternalIDP
wm.ForceMFA = e.ForceMFA
wm.PasswordlessType = e.PasswordlessType
case *policy.LoginPolicyRemovedEvent:
wm.IsActive = false
}
}
return wm.WriteModel.Reduce()
}
@@ -0,0 +1,26 @@
package command
import (
"github.com/caos/zitadel/internal/eventstore/v2"
"github.com/caos/zitadel/internal/v2/repository/policy"
)
type PolicyOrgIAMWriteModel struct {
eventstore.WriteModel
UserLoginMustBeDomain bool
IsActive bool
}
func (wm *PolicyOrgIAMWriteModel) Reduce() error {
for _, event := range wm.Events {
switch e := event.(type) {
case *policy.OrgIAMPolicyAddedEvent:
wm.UserLoginMustBeDomain = e.UserLoginMustBeDomain
wm.IsActive = true
case *policy.OrgIAMPolicyChangedEvent:
wm.UserLoginMustBeDomain = e.UserLoginMustBeDomain
}
}
return wm.WriteModel.Reduce()
}
@@ -0,0 +1,31 @@
package command
import (
"github.com/caos/zitadel/internal/eventstore/v2"
"github.com/caos/zitadel/internal/v2/repository/policy"
)
type PasswordAgePolicyWriteModel struct {
eventstore.WriteModel
ExpireWarnDays uint64
MaxAgeDays uint64
IsActive bool
}
func (wm *PasswordAgePolicyWriteModel) Reduce() error {
for _, event := range wm.Events {
switch e := event.(type) {
case *policy.PasswordAgePolicyAddedEvent:
wm.ExpireWarnDays = e.ExpireWarnDays
wm.MaxAgeDays = e.MaxAgeDays
wm.IsActive = true
case *policy.PasswordAgePolicyChangedEvent:
wm.ExpireWarnDays = e.ExpireWarnDays
wm.MaxAgeDays = e.MaxAgeDays
case *policy.PasswordAgePolicyRemovedEvent:
wm.IsActive = false
}
}
return wm.WriteModel.Reduce()
}
@@ -0,0 +1,40 @@
package command
import (
"github.com/caos/zitadel/internal/eventstore/v2"
"github.com/caos/zitadel/internal/v2/repository/policy"
)
type PasswordComplexityPolicyWriteModel struct {
eventstore.WriteModel
MinLength uint64
HasLowercase bool
HasUpperCase bool
HasNumber bool
HasSymbol bool
IsActive bool
}
func (wm *PasswordComplexityPolicyWriteModel) Reduce() error {
for _, event := range wm.Events {
switch e := event.(type) {
case *policy.PasswordComplexityPolicyAddedEvent:
wm.MinLength = e.MinLength
wm.HasLowercase = e.HasLowercase
wm.HasUpperCase = e.HasUpperCase
wm.HasNumber = e.HasNumber
wm.HasSymbol = e.HasSymbol
wm.IsActive = true
case *policy.PasswordComplexityPolicyChangedEvent:
wm.MinLength = e.MinLength
wm.HasLowercase = e.HasLowercase
wm.HasUpperCase = e.HasUpperCase
wm.HasNumber = e.HasNumber
wm.HasSymbol = e.HasSymbol
case *policy.PasswordComplexityPolicyRemovedEvent:
wm.IsActive = false
}
}
return wm.WriteModel.Reduce()
}
@@ -0,0 +1,31 @@
package command
import (
"github.com/caos/zitadel/internal/eventstore/v2"
"github.com/caos/zitadel/internal/v2/repository/policy"
)
type PasswordLockoutPolicyWriteModel struct {
eventstore.WriteModel
MaxAttempts uint64
ShowLockOutFailures bool
IsActive bool
}
func (wm *PasswordLockoutPolicyWriteModel) Reduce() error {
for _, event := range wm.Events {
switch e := event.(type) {
case *policy.PasswordLockoutPolicyAddedEvent:
wm.MaxAttempts = e.MaxAttempts
wm.ShowLockOutFailures = e.ShowLockOutFailures
wm.IsActive = true
case *policy.PasswordLockoutPolicyChangedEvent:
wm.MaxAttempts = e.MaxAttempts
wm.ShowLockOutFailures = e.ShowLockOutFailures
case *policy.PasswordLockoutPolicyRemovedEvent:
wm.IsActive = false
}
}
return wm.WriteModel.Reduce()
}
+65
View File
@@ -0,0 +1,65 @@
package command
import (
"context"
caos_errs "github.com/caos/zitadel/internal/errors"
iam_model "github.com/caos/zitadel/internal/iam/model"
"github.com/caos/zitadel/internal/v2/domain"
iam_repo "github.com/caos/zitadel/internal/v2/repository/iam"
)
func (r *CommandSide) StartSetup(ctx context.Context, iamID string, step domain.Step) (*iam_model.IAM, error) {
iamWriteModel, err := r.iamByID(ctx, iamID)
if err != nil && !caos_errs.IsNotFound(err) {
return nil, err
}
if iamWriteModel.SetUpStarted >= step || iamWriteModel.SetUpStarted != iamWriteModel.SetUpDone {
return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-9so34", "setup error")
}
aggregate := IAMAggregateFromWriteModel(&iamWriteModel.WriteModel).PushEvents(iam_repo.NewSetupStepStartedEvent(ctx, step))
err = r.eventstore.PushAggregate(ctx, iamWriteModel, aggregate)
if err != nil {
return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-Grgh1", "Setup start failed")
}
return writeModelToIAM(iamWriteModel), nil
}
//func (r *CommandSide) setupDone(ctx context.Context, iamAgg *iam_repo.Aggregate, event eventstore.EventPusher, aggregates ...eventstore.Aggregater) error {
// aggregate := iamAgg.PushEvents(event)
//
// aggregates = append(aggregates, aggregate)
// _, err := r.eventstore.PushAggregates(ctx, aggregates...)
// if err != nil {
// return caos_errs.ThrowPreconditionFailed(nil, "EVENT-Dgd2", "Setup done failed")
// }
// return nil
//}
//
////TODO: should not use readmodel
//func (r *CommandSide) setup(ctx context.Context, iamID string, step iam_repo.Step, event eventstore.EventPusher) (*iam_model.IAM, error) {
// iam, err := r.iamByID(ctx, iamID)
// if err != nil && !caos_errs.IsNotFound(err) {
// return nil, err
// }
//
// if iam != nil && (iam.SetUpStarted >= iam_repo.Step(step) || iam.SetUpStarted != iam.SetUpDone) {
// return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-9so34", "setup error")
// }
//
// aggregate := query.AggregateFromReadModel(iam).
// PushEvents(event)
//
// events, err := r.eventstore.PushAggregates(ctx, aggregate)
// if err != nil {
// return nil, err
// }
//
// if err = iam.AppendAndReduce(events...); err != nil {
// return nil, err
// }
// return nil, nil
// //TODO: return write model
// //return readModelToIAM(iam), nil
//}
+107
View File
@@ -0,0 +1,107 @@
package command
import (
"context"
caos_errs "github.com/caos/zitadel/internal/errors"
iam_model "github.com/caos/zitadel/internal/iam/model"
"github.com/caos/zitadel/internal/v2/domain"
iam_repo "github.com/caos/zitadel/internal/v2/repository/iam"
)
type Step1 struct {
GlobalOrg string
IAMProject string
DefaultLoginPolicy LoginPolicy //*iam_model.LoginPolicy
Orgs []Org
Owners []string
//setup *Setup
//createdUsers map[string]*usr_model.User
//createdOrgs map[string]*org_model.Org
//createdProjects map[string]*proj_model.Project
//pwComplexityPolicy *iam_model.PasswordComplexityPolicyView
}
type LoginPolicy struct {
AllowRegister bool
AllowUsernamePassword bool
AllowExternalIdp bool
}
type User struct {
FirstName string
LastName string
UserName string
Email string
Password string
}
type Org struct {
Name string
Domain string
OrgIamPolicy bool
Users []User
Owners []string
Projects []Project
}
type Project struct {
Name string
Users []User
Members []string
OIDCApps []OIDCApp
}
type OIDCApp struct {
Name string
RedirectUris []string
ResponseTypes []string
GrantTypes []string
ApplicationType string
AuthMethodType string
PostLogoutRedirectUris []string
DevMode bool
}
func (r *CommandSide) SetupStep1(ctx context.Context, iamID string, step1 Step1) error {
iam, err := r.iamByID(ctx, iamID)
if err != nil && !caos_errs.IsNotFound(err) {
return err
}
//create default login policy
iamAgg, err := r.addDefaultLoginPolicy(ctx,
NewIAMLoginPolicyWriteModel(iam.AggregateID),
&iam_model.LoginPolicy{
AllowUsernamePassword: step1.DefaultLoginPolicy.AllowUsernamePassword,
AllowRegister: step1.DefaultLoginPolicy.AllowRegister,
AllowExternalIdp: step1.DefaultLoginPolicy.AllowExternalIdp,
})
if err != nil {
return err
}
//create orgs
//create projects
//create applications
//set iam owners
//set global org
//set iam project id
/*aggregates:
iam:
default login policy
iam owner
org:
default
caos
zitadel
*/
iamAgg.PushEvents(iam_repo.NewSetupStepDoneEvent(ctx, domain.Step1))
_, err = r.eventstore.PushAggregates(ctx, iamAgg)
if err != nil {
return caos_errs.ThrowPreconditionFailed(nil, "EVENT-Gr2hh", "Setup Step1 failed")
}
return nil
}
+38
View File
@@ -0,0 +1,38 @@
package command
import (
"context"
caos_errs "github.com/caos/zitadel/internal/errors"
iam_model "github.com/caos/zitadel/internal/iam/model"
"github.com/caos/zitadel/internal/v2/domain"
iam_repo "github.com/caos/zitadel/internal/v2/repository/iam"
)
type Step2 struct {
DefaultPasswordComplexityPolicy iam_model.PasswordComplexityPolicy
}
func (r *CommandSide) SetupStep2(ctx context.Context, iamID string, step Step2) error {
iam, err := r.iamByID(ctx, iamID)
if err != nil && !caos_errs.IsNotFound(err) {
return err
}
iamAgg, err := r.addDefaultPasswordComplexityPolicy(ctx, NewIAMPasswordComplexityPolicyWriteModel(iam.AggregateID), &iam_model.PasswordComplexityPolicy{
MinLength: step.DefaultPasswordComplexityPolicy.MinLength,
HasLowercase: step.DefaultPasswordComplexityPolicy.HasLowercase,
HasUppercase: step.DefaultPasswordComplexityPolicy.HasUppercase,
HasNumber: step.DefaultPasswordComplexityPolicy.HasNumber,
HasSymbol: step.DefaultPasswordComplexityPolicy.HasSymbol,
})
if err != nil {
return err
}
iamAgg.PushEvents(iam_repo.NewSetupStepDoneEvent(ctx, domain.Step1))
_, err = r.eventstore.PushAggregates(ctx, iamAgg)
if err != nil {
return caos_errs.ThrowPreconditionFailed(nil, "EVENT-HR2na", "Setup Step2 failed")
}
return nil
}
+123
View File
@@ -0,0 +1,123 @@
package command
import (
"context"
caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/telemetry/tracing"
usr_model "github.com/caos/zitadel/internal/user/model"
"github.com/caos/zitadel/internal/v2/domain"
"github.com/caos/zitadel/internal/v2/repository/user"
)
func (r *CommandSide) AddUser(ctx context.Context, user *usr_model.User) (*usr_model.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)
if err != nil {
return nil, err
}
return &usr_model.User{UserName: user.UserName, Human: human}, nil
} else if user.Machine != nil {
}
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-8K0df", "Errors.User.TypeUndefined")
}
func (r *CommandSide) DeactivateUser(ctx context.Context, userID string) (*usr_model.User, error) {
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.UserStateInactive {
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-5M0sf", "Errors.User.AlreadyInactive")
}
userAgg := UserAggregateFromWriteModel(&existingUser.WriteModel)
userAgg.PushEvents(user.NewUserDeactivatedEvent(ctx))
err = r.eventstore.PushAggregate(ctx, existingUser, userAgg)
if err != nil {
return nil, err
}
return writeModelToUser(existingUser), nil
}
func (r *CommandSide) ReactivateUser(ctx context.Context, userID string) (*usr_model.User, error) {
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.UserStateInactive {
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-6M0sf", "Errors.User.NotInactive")
}
userAgg := UserAggregateFromWriteModel(&existingUser.WriteModel)
userAgg.PushEvents(user.NewUserReactivatedEvent(ctx))
err = r.eventstore.PushAggregate(ctx, existingUser, userAgg)
if err != nil {
return nil, err
}
return writeModelToUser(existingUser), nil
}
func (r *CommandSide) LockUser(ctx context.Context, userID string) (*usr_model.User, error) {
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.UserStateActive && existingUser.UserState != domain.UserStateInitial {
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-2M9fs", "Errors.User.ShouldBeActiveOrInitial")
}
userAgg := UserAggregateFromWriteModel(&existingUser.WriteModel)
userAgg.PushEvents(user.NewUserLockedEvent(ctx))
err = r.eventstore.PushAggregate(ctx, existingUser, userAgg)
if err != nil {
return nil, err
}
return writeModelToUser(existingUser), nil
}
func (r *CommandSide) UnlockUser(ctx context.Context, userID string) (*usr_model.User, error) {
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.UserStateLocked {
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-4M0ds", "Errors.User.NotLocked")
}
userAgg := UserAggregateFromWriteModel(&existingUser.WriteModel)
userAgg.PushEvents(user.NewUserUnlockedEvent(ctx))
err = r.eventstore.PushAggregate(ctx, existingUser, userAgg)
if err != nil {
return nil, err
}
return writeModelToUser(existingUser), nil
}
func (r *CommandSide) userWriteModelByID(ctx context.Context, userID string) (writeModel *UserWriteModel, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
writeModel = NewUserWriteModel(userID)
err = r.eventstore.FilterToQueryReducer(ctx, writeModel)
if err != nil {
return nil, err
}
return writeModel, nil
}
+62
View File
@@ -0,0 +1,62 @@
package command
import (
"github.com/caos/zitadel/internal/user/model"
)
func writeModelToUser(wm *UserWriteModel) *model.User {
return &model.User{
ObjectRoot: writeModelToObjectRoot(wm.WriteModel),
UserName: wm.UserName,
State: model.UserState(wm.UserState),
}
}
func writeModelToHuman(wm *HumanWriteModel) *model.Human {
return &model.Human{
ObjectRoot: writeModelToObjectRoot(wm.WriteModel),
Profile: &model.Profile{
FirstName: wm.FirstName,
LastName: wm.LastName,
NickName: wm.NickName,
DisplayName: wm.DisplayName,
PreferredLanguage: wm.PreferredLanguage,
Gender: model.Gender(wm.Gender),
},
Email: &model.Email{
EmailAddress: wm.Email,
IsEmailVerified: wm.IsEmailVerified,
},
}
}
func writeModelToProfile(wm *HumanProfileWriteModel) *model.Profile {
return &model.Profile{
ObjectRoot: writeModelToObjectRoot(wm.WriteModel),
FirstName: wm.FirstName,
LastName: wm.LastName,
NickName: wm.NickName,
DisplayName: wm.DisplayName,
PreferredLanguage: wm.PreferredLanguage,
Gender: model.Gender(wm.Gender),
}
}
func writeModelToEmail(wm *HumanEmailWriteModel) *model.Email {
return &model.Email{
ObjectRoot: writeModelToObjectRoot(wm.WriteModel),
EmailAddress: wm.Email,
IsEmailVerified: wm.IsEmailVerified,
}
}
func writeModelToAddress(wm *HumanAddressWriteModel) *model.Address {
return &model.Address{
ObjectRoot: writeModelToObjectRoot(wm.WriteModel),
Country: wm.Country,
Locality: wm.Locality,
PostalCode: wm.PostalCode,
Region: wm.Region,
StreetAddress: wm.StreetAddress,
}
}
+71
View File
@@ -0,0 +1,71 @@
package command
import (
"context"
caos_errs "github.com/caos/zitadel/internal/errors"
usr_model "github.com/caos/zitadel/internal/user/model"
"github.com/caos/zitadel/internal/v2/domain"
"github.com/caos/zitadel/internal/v2/repository/user"
)
func (r *CommandSide) AddHuman(ctx context.Context, orgID, username string, human *usr_model.Human) (*usr_model.Human, error) {
if !human.IsValid() {
return nil, caos_errs.ThrowInvalidArgument(nil, "COMMAND-4M90d", "Errors.User.Invalid")
}
userID, err := r.idGenerator.Next()
if err != nil {
return nil, err
}
human.AggregateID = userID
orgIAMPolicy, err := r.GetOrgIAMPolicy(ctx, orgID)
if err != nil {
return nil, err
}
//pwPolicy, err := r.GetOrgPasswordComplexityPolicy(ctx, orgID)
//if err != nil {
// return nil, err
//}
addedHuman := NewHumanWriteModel(human.AggregateID)
//TODO: Check Unique Username
human.CheckOrgIAMPolicy(username, orgIAMPolicy)
human.SetNamesAsDisplayname()
//human.HashPasswordIfExisting(pwPolicy, r.userPasswordAlg, true)
userAgg := UserAggregateFromWriteModel(&addedHuman.WriteModel)
userAgg.PushEvents(
user.NewHumanAddedEvent(
ctx,
username,
human.FirstName,
human.LastName,
human.NickName,
human.DisplayName,
human.PreferredLanguage,
domain.Gender(human.Gender),
human.EmailAddress,
human.PhoneNumber,
human.Country,
human.Locality,
human.PostalCode,
human.Region,
human.StreetAddress,
),
)
//TODO: HashPassword If existing
//TODO: Generate Init Code if needed
//TODO: Generate Phone Code if needed
if human.Email != nil && human.EmailAddress != "" && human.IsEmailVerified {
userAgg.PushEvents(user.NewHumanEmailVerifiedEvent(ctx))
}
if human.Phone != nil && human.PhoneNumber != "" && human.IsPhoneVerified {
userAgg.PushEvents(user.NewHumanPhoneVerifiedEvent(ctx))
}
err = r.eventstore.PushAggregate(ctx, addedHuman, userAgg)
if err != nil {
return nil, err
}
return writeModelToHuman(addedHuman), nil
}
+44
View File
@@ -0,0 +1,44 @@
package command
import (
"context"
caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/telemetry/tracing"
usr_model "github.com/caos/zitadel/internal/user/model"
"github.com/caos/zitadel/internal/v2/domain"
)
func (r *CommandSide) ChangeHumanAddress(ctx context.Context, address *usr_model.Address) (*usr_model.Address, error) {
existingAddress, err := r.addressWriteModel(ctx, address.AggregateID)
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")
}
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")
}
userAgg := UserAggregateFromWriteModel(&existingAddress.WriteModel)
userAgg.PushEvents(changedEvent)
err = r.eventstore.PushAggregate(ctx, existingAddress, userAgg)
if err != nil {
return nil, err
}
return writeModelToAddress(existingAddress), nil
}
func (r *CommandSide) addressWriteModel(ctx context.Context, userID string) (writeModel *HumanAddressWriteModel, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
writeModel = NewHumanAddressWriteModel(userID)
err = r.eventstore.FilterToQueryReducer(ctx, writeModel)
if err != nil {
return nil, err
}
return writeModel, nil
}
@@ -0,0 +1,109 @@
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 HumanAddressWriteModel struct {
eventstore.WriteModel
Country string
Locality string
PostalCode string
Region string
StreetAddress string
UserState domain.UserState
}
func NewHumanAddressWriteModel(userID string) *HumanAddressWriteModel {
return &HumanAddressWriteModel{
WriteModel: eventstore.WriteModel{
AggregateID: userID,
},
}
}
func (wm *HumanAddressWriteModel) AppendEvents(events ...eventstore.EventReader) {
for _, event := range events {
switch e := event.(type) {
case *user.HumanAddressChangedEvent:
wm.AppendEvents(e)
case *user.HumanAddedEvent, *user.HumanRegisteredEvent:
wm.AppendEvents(e)
case *user.UserRemovedEvent:
wm.AppendEvents(e)
}
}
}
func (wm *HumanAddressWriteModel) Reduce() error {
for _, event := range wm.Events {
switch e := event.(type) {
case *user.HumanAddedEvent:
wm.Country = e.Country
wm.Locality = e.Locality
wm.PostalCode = e.PostalCode
wm.Region = e.Region
wm.StreetAddress = e.StreetAddress
wm.UserState = domain.UserStateActive
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
case *user.HumanAddressChangedEvent:
wm.Country = e.Country
wm.Locality = e.Locality
wm.PostalCode = e.PostalCode
wm.Region = e.Region
wm.StreetAddress = e.StreetAddress
case *user.UserRemovedEvent:
wm.UserState = domain.UserStateDeleted
}
}
return wm.WriteModel.Reduce()
}
func (wm *HumanAddressWriteModel) Query() *eventstore.SearchQueryBuilder {
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, user.AggregateType).
AggregateIDs(wm.AggregateID)
}
func (wm *HumanAddressWriteModel) NewChangedEvent(
ctx context.Context,
country,
locality,
postalCode,
region,
streetAddress string,
) (*user.HumanAddressChangedEvent, bool) {
hasChanged := false
changedEvent := user.NewHumanAddressChangedEvent(ctx)
if wm.Country != country {
hasChanged = true
changedEvent.Country = country
}
if wm.Locality != locality {
hasChanged = true
changedEvent.Locality = locality
}
if wm.PostalCode != postalCode {
hasChanged = true
changedEvent.PostalCode = postalCode
}
if wm.Region != region {
hasChanged = true
changedEvent.Region = region
}
if wm.StreetAddress != streetAddress {
hasChanged = true
changedEvent.StreetAddress = streetAddress
}
return changedEvent, hasChanged
}
+48
View File
@@ -0,0 +1,48 @@
package command
import (
"context"
caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/telemetry/tracing"
usr_model "github.com/caos/zitadel/internal/user/model"
"github.com/caos/zitadel/internal/v2/domain"
)
func (r *CommandSide) ChangeHumanEmail(ctx context.Context, email *usr_model.Email) (*usr_model.Email, error) {
if !email.IsValid() {
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-4M9sf", "Errors.Email.Invalid")
}
existingEmail, err := r.emailWriteModel(ctx, email.AggregateID)
if err != nil {
return nil, err
}
if existingEmail.UserState == domain.UserStateUnspecified || existingEmail.UserState == domain.UserStateDeleted {
return nil, caos_errs.ThrowAlreadyExists(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")
}
userAgg := UserAggregateFromWriteModel(&existingEmail.WriteModel)
userAgg.PushEvents(changedEvent)
err = r.eventstore.PushAggregate(ctx, existingEmail, userAgg)
if err != nil {
return nil, err
}
return writeModelToEmail(existingEmail), nil
}
func (r *CommandSide) emailWriteModel(ctx context.Context, userID string) (writeModel *HumanEmailWriteModel, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
writeModel = NewHumanEmailWriteModel(userID)
err = r.eventstore.FilterToQueryReducer(ctx, writeModel)
if err != nil {
return nil, err
}
return writeModel, nil
}
@@ -0,0 +1,79 @@
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 HumanEmailWriteModel struct {
eventstore.WriteModel
Email string
IsEmailVerified bool
UserState domain.UserState
}
func NewHumanEmailWriteModel(userID string) *HumanEmailWriteModel {
return &HumanEmailWriteModel{
WriteModel: eventstore.WriteModel{
AggregateID: userID,
},
}
}
func (wm *HumanEmailWriteModel) 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.UserRemovedEvent:
wm.AppendEvents(e)
}
}
}
func (wm *HumanEmailWriteModel) Reduce() error {
for _, event := range wm.Events {
switch e := event.(type) {
case *user.HumanAddedEvent:
wm.Email = e.EmailAddress
wm.UserState = domain.UserStateActive
case *user.HumanRegisteredEvent:
wm.Email = e.EmailAddress
wm.UserState = domain.UserStateActive
case *user.HumanEmailChangedEvent:
wm.Email = e.EmailAddress
wm.IsEmailVerified = false
case *user.HumanEmailVerifiedEvent:
wm.IsEmailVerified = true
case *user.UserRemovedEvent:
wm.UserState = domain.UserStateDeleted
}
}
return wm.WriteModel.Reduce()
}
func (wm *HumanEmailWriteModel) Query() *eventstore.SearchQueryBuilder {
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, user.AggregateType).
AggregateIDs(wm.AggregateID)
}
func (wm *HumanEmailWriteModel) NewChangedEvent(
ctx context.Context,
email string,
) (*user.HumanEmailChangedEvent, bool) {
hasChanged := false
changedEvent := user.NewHumanEmailChangedEvent(ctx)
if wm.Email != email {
hasChanged = true
changedEvent.EmailAddress = email
}
return changedEvent, hasChanged
}
+104
View File
@@ -0,0 +1,104 @@
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"
"golang.org/x/text/language"
)
type HumanWriteModel struct {
eventstore.WriteModel
UserName string
FirstName string
LastName string
NickName string
DisplayName string
PreferredLanguage language.Tag
Gender domain.Gender
Email string
IsEmailVerified bool
Phone string
IsPhoneVerified bool
Country string
Locality string
PostalCode string
Region string
StreetAddress string
UserState domain.UserState
}
func NewHumanWriteModel(userID string) *HumanWriteModel {
return &HumanWriteModel{
WriteModel: eventstore.WriteModel{
AggregateID: userID,
},
}
}
func (wm *HumanWriteModel) 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.UserDeactivatedEvent:
wm.AppendEvents(e)
case *user.UserReactivatedEvent:
wm.AppendEvents(e)
case *user.UserLockedEvent:
wm.AppendEvents(e)
case *user.UserUnlockedEvent:
wm.AppendEvents(e)
case *user.UserRemovedEvent:
wm.AppendEvents(e)
}
}
}
//TODO: Compute State? initial/active
func (wm *HumanWriteModel) Reduce() error {
for _, event := range wm.Events {
switch e := event.(type) {
case *user.HumanAddedEvent:
wm.UserName = e.UserName
wm.UserState = domain.UserStateInitial
case *user.HumanRegisteredEvent:
wm.UserName = e.UserName
wm.UserState = domain.UserStateInitial
case *user.UserLockedEvent:
if wm.UserState != domain.UserStateDeleted {
wm.UserState = domain.UserStateLocked
}
case *user.UserUnlockedEvent:
if wm.UserState != domain.UserStateDeleted {
wm.UserState = domain.UserStateActive
}
case *user.UserDeactivatedEvent:
if wm.UserState != domain.UserStateDeleted {
wm.UserState = domain.UserStateInactive
}
case *user.UserReactivatedEvent:
if wm.UserState != domain.UserStateDeleted {
wm.UserState = domain.UserStateActive
}
case *user.UserRemovedEvent:
wm.UserState = domain.UserStateDeleted
}
}
return wm.WriteModel.Reduce()
}
func (wm *HumanWriteModel) Query() *eventstore.SearchQueryBuilder {
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, user.AggregateType).
AggregateIDs(wm.AggregateID)
}
+48
View File
@@ -0,0 +1,48 @@
package command
import (
"context"
caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/telemetry/tracing"
usr_model "github.com/caos/zitadel/internal/user/model"
"github.com/caos/zitadel/internal/v2/domain"
)
func (r *CommandSide) ChangeHumanProfile(ctx context.Context, profile *usr_model.Profile) (*usr_model.Profile, error) {
if !profile.IsValid() {
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-8io0d", "Errors.User.Profile.Invalid")
}
existingProfile, err := r.profileWriteModelByID(ctx, profile.AggregateID)
if err != nil {
return nil, err
}
if existingProfile.UserState == domain.UserStateUnspecified || existingProfile.UserState == domain.UserStateDeleted {
return nil, caos_errs.ThrowAlreadyExists(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")
}
userAgg := UserAggregateFromWriteModel(&existingProfile.WriteModel)
userAgg.PushEvents(changedEvent)
err = r.eventstore.PushAggregate(ctx, existingProfile, userAgg)
if err != nil {
return nil, err
}
return writeModelToProfile(existingProfile), nil
}
func (r *CommandSide) profileWriteModelByID(ctx context.Context, userID string) (writeModel *HumanProfileWriteModel, err error) {
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
writeModel = NewHumanProfileWriteModel(userID)
err = r.eventstore.FilterToQueryReducer(ctx, writeModel)
if err != nil {
return nil, err
}
return writeModel, nil
}
@@ -0,0 +1,120 @@
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"
"golang.org/x/text/language"
)
type HumanProfileWriteModel struct {
eventstore.WriteModel
FirstName string
LastName string
NickName string
DisplayName string
PreferredLanguage language.Tag
Gender domain.Gender
UserState domain.UserState
}
func NewHumanProfileWriteModel(userID string) *HumanProfileWriteModel {
return &HumanProfileWriteModel{
WriteModel: eventstore.WriteModel{
AggregateID: userID,
},
}
}
func (wm *HumanProfileWriteModel) AppendEvents(events ...eventstore.EventReader) {
for _, event := range events {
switch e := event.(type) {
case *user.HumanProfileChangedEvent:
wm.AppendEvents(e)
case *user.HumanAddedEvent, *user.HumanRegisteredEvent:
wm.AppendEvents(e)
case *user.UserRemovedEvent:
wm.AppendEvents(e)
}
}
}
func (wm *HumanProfileWriteModel) Reduce() error {
for _, event := range wm.Events {
switch e := event.(type) {
case *user.HumanAddedEvent:
wm.FirstName = e.FirstName
wm.LastName = e.LastName
wm.NickName = e.NickName
wm.DisplayName = e.DisplayName
wm.PreferredLanguage = e.PreferredLanguage
wm.Gender = e.Gender
wm.UserState = domain.UserStateActive
case *user.HumanRegisteredEvent:
wm.FirstName = e.FirstName
wm.LastName = e.LastName
wm.NickName = e.NickName
wm.DisplayName = e.DisplayName
wm.PreferredLanguage = e.PreferredLanguage
wm.Gender = e.Gender
wm.UserState = domain.UserStateActive
case *user.HumanProfileChangedEvent:
wm.FirstName = e.FirstName
wm.LastName = e.LastName
wm.NickName = e.NickName
wm.DisplayName = e.DisplayName
wm.PreferredLanguage = e.PreferredLanguage
wm.Gender = e.Gender
case *user.UserRemovedEvent:
wm.UserState = domain.UserStateDeleted
}
}
return wm.WriteModel.Reduce()
}
func (wm *HumanProfileWriteModel) Query() *eventstore.SearchQueryBuilder {
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, user.AggregateType).
AggregateIDs(wm.AggregateID)
}
func (wm *HumanProfileWriteModel) NewChangedEvent(
ctx context.Context,
firstName,
lastName,
nickName,
displayName string,
preferredLanguage language.Tag,
gender domain.Gender,
) (*user.HumanProfileChangedEvent, bool) {
hasChanged := false
changedEvent := user.NewHumanProfileChangedEvent(ctx)
if wm.FirstName != firstName {
hasChanged = true
changedEvent.FirstName = firstName
}
if wm.LastName != lastName {
hasChanged = true
changedEvent.LastName = lastName
}
if wm.NickName != nickName {
hasChanged = true
changedEvent.NickName = nickName
}
if wm.DisplayName != displayName {
hasChanged = true
changedEvent.DisplayName = displayName
}
if wm.PreferredLanguage != preferredLanguage {
hasChanged = true
changedEvent.PreferredLanguage = preferredLanguage
}
if gender.Valid() && wm.Gender != gender {
hasChanged = true
changedEvent.Gender = gender
}
return changedEvent, hasChanged
}
+95
View File
@@ -0,0 +1,95 @@
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 UserWriteModel struct {
eventstore.WriteModel
UserName string
UserState domain.UserState
}
func NewUserWriteModel(userID string) *UserWriteModel {
return &UserWriteModel{
WriteModel: eventstore.WriteModel{
AggregateID: userID,
},
}
}
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.UserDeactivatedEvent:
wm.AppendEvents(e)
case *user.UserReactivatedEvent:
wm.AppendEvents(e)
case *user.UserLockedEvent:
wm.AppendEvents(e)
case *user.UserUnlockedEvent:
wm.AppendEvents(e)
case *user.UserRemovedEvent:
wm.AppendEvents(e)
}
}
}
//TODO: Compute State? initial/active
func (wm *UserWriteModel) Reduce() error {
for _, event := range wm.Events {
switch e := event.(type) {
case *user.HumanAddedEvent:
wm.UserName = e.UserName
wm.UserState = domain.UserStateInitial
case *user.HumanRegisteredEvent:
wm.UserName = e.UserName
wm.UserState = domain.UserStateInitial
case *user.MachineAddedEvent:
wm.UserName = e.UserName
wm.UserState = domain.UserStateActive
case *user.UserLockedEvent:
if wm.UserState != domain.UserStateDeleted {
wm.UserState = domain.UserStateLocked
}
case *user.UserUnlockedEvent:
if wm.UserState != domain.UserStateDeleted {
wm.UserState = domain.UserStateActive
}
case *user.UserDeactivatedEvent:
if wm.UserState != domain.UserStateDeleted {
wm.UserState = domain.UserStateInactive
}
case *user.UserReactivatedEvent:
if wm.UserState != domain.UserStateDeleted {
wm.UserState = domain.UserStateActive
}
case *user.UserRemovedEvent:
wm.UserState = domain.UserStateDeleted
}
}
return wm.WriteModel.Reduce()
}
func (wm *UserWriteModel) Query() *eventstore.SearchQueryBuilder {
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, user.AggregateType).
AggregateIDs(wm.AggregateID)
}
func UserAggregateFromWriteModel(wm *eventstore.WriteModel) *user.Aggregate {
return &user.Aggregate{
Aggregate: *eventstore.AggregateFromWriteModel(wm, user.AggregateType, user.AggregateVersion),
}
}