From 31ea9d1acd8367e78465388610b24ed0fb0eea1f Mon Sep 17 00:00:00 2001 From: Fabi <38692350+fgerschwiler@users.noreply.github.com> Date: Thu, 10 Dec 2020 16:18:52 +0100 Subject: [PATCH] feat: user events (#1062) * feat: user new eventstore * feat: rename query builder * feat: human events * feat: human events * feat: events * feat: phone events * feat: phone events * feat: profile, address events * feat: mfa, otp * feat: webauthn events * feat: webauthn events * feat: webauthn events * feat: enums * feat: new events * feat: user events * feat: domain events * feat: all v2 events * feat: all v1 events * feat: pkg structure * feat: change events * feat: better naming * feat: better naming --- .../repository/eventsourcing/model/types.go | 6 +- internal/v2/repository/user/aggregate.go | 49 ++ internal/v2/repository/user/events.go | 321 ++++++++++++ internal/v2/repository/user/eventstore.go | 114 +++++ .../repository/user/human/address/events.go | 79 +++ .../user/human/address/write_model.go | 13 + .../v2/repository/user/human/email/events.go | 189 +++++++ .../user/human/email/write_model.go | 9 + internal/v2/repository/user/human/events.go | 336 +++++++++++++ .../user/human/external_idp/events.go | 206 ++++++++ internal/v2/repository/user/human/human.go | 16 + .../v2/repository/user/human/mfa/events.go | 39 ++ internal/v2/repository/user/human/mfa/mfa.go | 15 + .../repository/user/human/mfa/otp/events.go | 168 +++++++ .../user/human/mfa/web_auth_n/events.go | 426 ++++++++++++++++ .../v2/repository/user/human/notification.go | 8 + .../repository/user/human/password/events.go | 189 +++++++ .../v2/repository/user/human/phone/events.go | 210 ++++++++ .../repository/user/human/profile/events.go | 84 ++++ .../user/human/profile/write_model.go | 18 + internal/v2/repository/user/machine/events.go | 107 ++++ .../v2/repository/user/machine/keys/events.go | 103 ++++ .../v2/repository/user/machine/keys/key.go | 14 + internal/v2/repository/user/read_model.go | 52 ++ internal/v2/repository/user/v1/events.go | 469 ++++++++++++++++++ 25 files changed, 3237 insertions(+), 3 deletions(-) create mode 100644 internal/v2/repository/user/aggregate.go create mode 100644 internal/v2/repository/user/events.go create mode 100644 internal/v2/repository/user/eventstore.go create mode 100644 internal/v2/repository/user/human/address/events.go create mode 100644 internal/v2/repository/user/human/address/write_model.go create mode 100644 internal/v2/repository/user/human/email/events.go create mode 100644 internal/v2/repository/user/human/email/write_model.go create mode 100644 internal/v2/repository/user/human/events.go create mode 100644 internal/v2/repository/user/human/external_idp/events.go create mode 100644 internal/v2/repository/user/human/human.go create mode 100644 internal/v2/repository/user/human/mfa/events.go create mode 100644 internal/v2/repository/user/human/mfa/mfa.go create mode 100644 internal/v2/repository/user/human/mfa/otp/events.go create mode 100644 internal/v2/repository/user/human/mfa/web_auth_n/events.go create mode 100644 internal/v2/repository/user/human/notification.go create mode 100644 internal/v2/repository/user/human/password/events.go create mode 100644 internal/v2/repository/user/human/phone/events.go create mode 100644 internal/v2/repository/user/human/profile/events.go create mode 100644 internal/v2/repository/user/human/profile/write_model.go create mode 100644 internal/v2/repository/user/machine/events.go create mode 100644 internal/v2/repository/user/machine/keys/events.go create mode 100644 internal/v2/repository/user/machine/keys/key.go create mode 100644 internal/v2/repository/user/read_model.go create mode 100644 internal/v2/repository/user/v1/events.go diff --git a/internal/user/repository/eventsourcing/model/types.go b/internal/user/repository/eventsourcing/model/types.go index f392fd997c..c67b09fe25 100644 --- a/internal/user/repository/eventsourcing/model/types.go +++ b/internal/user/repository/eventsourcing/model/types.go @@ -52,9 +52,6 @@ const ( MFAInitSkipped models.EventType = "user.mfa.init.skipped" SignedOut models.EventType = "user.signed.out" - - DomainClaimed models.EventType = "user.domain.claimed" - DomainClaimedSent models.EventType = "user.domain.claimed.sent" ) //the following consts are for user(v2) @@ -69,6 +66,9 @@ const ( UserRemoved models.EventType = "user.removed" UserTokenAdded models.EventType = "user.token.added" + + DomainClaimed models.EventType = "user.domain.claimed" + DomainClaimedSent models.EventType = "user.domain.claimed.sent" ) // the following consts are for user(v2).human diff --git a/internal/v2/repository/user/aggregate.go b/internal/v2/repository/user/aggregate.go new file mode 100644 index 0000000000..b002e71fc2 --- /dev/null +++ b/internal/v2/repository/user/aggregate.go @@ -0,0 +1,49 @@ +package user + +import ( + "github.com/caos/zitadel/internal/eventstore/v2" +) + +const ( + AggregateType = "user" + AggregateVersion = "v2" +) + +type Aggregate struct { + eventstore.Aggregate +} + +func NewAggregate( + id, + resourceOwner string, + previousSequence uint64, +) *Aggregate { + + return &Aggregate{ + Aggregate: *eventstore.NewAggregate( + id, + AggregateType, + resourceOwner, + AggregateVersion, + previousSequence, + ), + } +} + +func AggregateFromWriteModel(wm *eventstore.WriteModel) *Aggregate { + return &Aggregate{ + Aggregate: *eventstore.AggregateFromWriteModel(wm, AggregateType, AggregateVersion), + } +} + +func AggregateFromReadModel(rm *ReadModel) *Aggregate { + return &Aggregate{ + Aggregate: *eventstore.NewAggregate( + rm.AggregateID, + AggregateType, + rm.ResourceOwner, + AggregateVersion, + rm.ProcessedSequence, + ), + } +} diff --git a/internal/v2/repository/user/events.go b/internal/v2/repository/user/events.go new file mode 100644 index 0000000000..7f7919bef5 --- /dev/null +++ b/internal/v2/repository/user/events.go @@ -0,0 +1,321 @@ +package user + +import ( + "context" + "encoding/json" + "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/v2" + "github.com/caos/zitadel/internal/eventstore/v2/repository" + "time" +) + +const ( + userEventTypePrefix = eventstore.EventType("user.") + UserLockedType = userEventTypePrefix + "locked" + UserUnlockedType = userEventTypePrefix + "unlocked" + UserDeactivatedType = userEventTypePrefix + "deactivated" + UserReactivatedType = userEventTypePrefix + "reactivated" + UserRemovedType = userEventTypePrefix + "removed" + UserTokenAddedType = userEventTypePrefix + "token.added" + UserDomainClaimedType = userEventTypePrefix + "domain.claimed" + UserDomainClaimedSentType = userEventTypePrefix + "domain.claimed.sent" + UserUserNameChangedType = userEventTypePrefix + "username.changed" +) + +type LockedEvent struct { + eventstore.BaseEvent `json:"-"` +} + +func (e *LockedEvent) CheckPrevious() bool { + return true +} + +func (e *LockedEvent) Data() interface{} { + return nil +} + +func NewLockedEvent(ctx context.Context) *LockedEvent { + return &LockedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + UserLockedType, + ), + } +} + +func LockedEventMapper(event *repository.Event) (eventstore.EventReader, error) { + return &LockedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + }, nil +} + +type UnlockedEvent struct { + eventstore.BaseEvent `json:"-"` +} + +func (e *UnlockedEvent) CheckPrevious() bool { + return true +} + +func (e *UnlockedEvent) Data() interface{} { + return nil +} + +func NewUnlockedEvent(ctx context.Context) *UnlockedEvent { + return &UnlockedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + UserUnlockedType, + ), + } +} + +func UnlockedEventMapper(event *repository.Event) (eventstore.EventReader, error) { + return &UnlockedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + }, nil +} + +type DeactivatedEvent struct { + eventstore.BaseEvent `json:"-"` +} + +func (e *DeactivatedEvent) CheckPrevious() bool { + return true +} + +func (e *DeactivatedEvent) Data() interface{} { + return nil +} + +func NewDeactivatedEvent(ctx context.Context) *DeactivatedEvent { + return &DeactivatedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + UserDeactivatedType, + ), + } +} + +func DeactivatedEventMapper(event *repository.Event) (eventstore.EventReader, error) { + return &DeactivatedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + }, nil +} + +type ReactivatedEvent struct { + eventstore.BaseEvent `json:"-"` +} + +func (e *ReactivatedEvent) CheckPrevious() bool { + return true +} + +func (e *ReactivatedEvent) Data() interface{} { + return nil +} + +func NewReactivatedEvent(ctx context.Context) *ReactivatedEvent { + return &ReactivatedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + UserReactivatedType, + ), + } +} + +func ReactivatedEventMapper(event *repository.Event) (eventstore.EventReader, error) { + return &ReactivatedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + }, nil +} + +type RemovedEvent struct { + eventstore.BaseEvent `json:"-"` +} + +func (e *RemovedEvent) CheckPrevious() bool { + return true +} + +func (e *RemovedEvent) Data() interface{} { + return nil +} + +func NewRemovedEvent(ctx context.Context) *RemovedEvent { + return &RemovedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + UserRemovedType, + ), + } +} + +func RemovedEventMapper(event *repository.Event) (eventstore.EventReader, error) { + return &RemovedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + }, nil +} + +type TokenAddedEvent struct { + eventstore.BaseEvent `json:"-"` + + TokenID string `json:"tokenId"` + ApplicationID string `json:"applicationId"` + UserAgentID string `json:"userAgentId"` + Audience []string `json:"audience"` + Scopes []string `json:"scopes""` + Expiration time.Time `json:"expiration"` + PreferredLanguage string `json:"preferredLanguage"` +} + +func (e *TokenAddedEvent) CheckPrevious() bool { + return false +} + +func (e *TokenAddedEvent) Data() interface{} { + return e +} + +func NewTokenAddedEvent( + ctx context.Context, + tokenID, + applicationID, + userAgentID, + preferredLanguage string, + audience, + scopes []string, + expiration time.Time, +) *TokenAddedEvent { + return &TokenAddedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + UserTokenAddedType, + ), + TokenID: tokenID, + ApplicationID: applicationID, + UserAgentID: userAgentID, + Audience: audience, + Scopes: scopes, + Expiration: expiration, + } +} + +func TokenAddedEventMapper(event *repository.Event) (eventstore.EventReader, error) { + tokenAdded := &TokenAddedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + } + err := json.Unmarshal(event.Data, tokenAdded) + if err != nil { + return nil, errors.ThrowInternal(err, "USER-7M9sd", "unable to unmarshal token added") + } + + return tokenAdded, nil +} + +type DomainClaimedEvent struct { + eventstore.BaseEvent `json:"-"` + + UserName string `json:"userName"` +} + +func (e *DomainClaimedEvent) CheckPrevious() bool { + return false +} + +func (e *DomainClaimedEvent) Data() interface{} { + return e +} + +func NewDomainClaimedEvent( + ctx context.Context, + userName string, +) *DomainClaimedEvent { + return &DomainClaimedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + UserDomainClaimedType, + ), + UserName: userName, + } +} + +func DomainClaimedEventMapper(event *repository.Event) (eventstore.EventReader, error) { + domainClaimed := &DomainClaimedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + } + err := json.Unmarshal(event.Data, domainClaimed) + if err != nil { + return nil, errors.ThrowInternal(err, "USER-aR8jc", "unable to unmarshal domain claimed") + } + + return domainClaimed, nil +} + +type DomainClaimedSentEvent struct { + eventstore.BaseEvent `json:"-"` +} + +func (e *DomainClaimedSentEvent) CheckPrevious() bool { + return false +} + +func (e *DomainClaimedSentEvent) Data() interface{} { + return nil +} + +func NewDomainClaimedSentEvent( + ctx context.Context, +) *DomainClaimedSentEvent { + return &DomainClaimedSentEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + UserDomainClaimedSentType, + ), + } +} + +func DomainClaimedSentEventMapper(event *repository.Event) (eventstore.EventReader, error) { + return &DomainClaimedSentEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + }, nil +} + +type UsernameChangedEvent struct { + eventstore.BaseEvent `json:"-"` + + UserName string `json:"userName"` +} + +func (e *UsernameChangedEvent) CheckPrevious() bool { + return false +} + +func (e *UsernameChangedEvent) Data() interface{} { + return e +} + +func NewUsernameChangedEvent( + ctx context.Context, + userName string, +) *UsernameChangedEvent { + return &UsernameChangedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + UserUserNameChangedType, + ), + UserName: userName, + } +} + +func UsernameChangedEventMapper(event *repository.Event) (eventstore.EventReader, error) { + domainClaimed := &UsernameChangedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + } + err := json.Unmarshal(event.Data, domainClaimed) + if err != nil { + return nil, errors.ThrowInternal(err, "USER-4Bm9s", "unable to unmarshal username changed") + } + + return domainClaimed, nil +} diff --git a/internal/v2/repository/user/eventstore.go b/internal/v2/repository/user/eventstore.go new file mode 100644 index 0000000000..73e2ecf869 --- /dev/null +++ b/internal/v2/repository/user/eventstore.go @@ -0,0 +1,114 @@ +package user + +import ( + "github.com/caos/zitadel/internal/eventstore/v2" + "github.com/caos/zitadel/internal/v2/repository/user/human" + "github.com/caos/zitadel/internal/v2/repository/user/human/address" + "github.com/caos/zitadel/internal/v2/repository/user/human/email" + "github.com/caos/zitadel/internal/v2/repository/user/human/external_idp" + "github.com/caos/zitadel/internal/v2/repository/user/human/mfa" + "github.com/caos/zitadel/internal/v2/repository/user/human/mfa/otp" + "github.com/caos/zitadel/internal/v2/repository/user/human/mfa/web_auth_n" + "github.com/caos/zitadel/internal/v2/repository/user/human/password" + "github.com/caos/zitadel/internal/v2/repository/user/human/phone" + "github.com/caos/zitadel/internal/v2/repository/user/human/profile" + "github.com/caos/zitadel/internal/v2/repository/user/machine" + "github.com/caos/zitadel/internal/v2/repository/user/machine/keys" + "github.com/caos/zitadel/internal/v2/repository/user/v1" +) + +func RegisterEventMappers(es *eventstore.Eventstore) { + es.RegisterFilterEventMapper(v1.UserV1AddedType, human.AddedEventMapper). + RegisterFilterEventMapper(v1.UserV1RegisteredType, human.RegisteredEventMapper). + RegisterFilterEventMapper(v1.UserV1InitialCodeAddedType, human.InitialCodeAddedEventMapper). + RegisterFilterEventMapper(v1.UserV1InitialCodeSentType, human.InitialCodeSentEventMapper). + RegisterFilterEventMapper(v1.UserV1InitializedCheckSucceededType, human.InitializedCheckSucceededEventMapper). + RegisterFilterEventMapper(v1.UserV1InitializedCheckFailedType, human.InitializedCheckFailedEventMapper). + RegisterFilterEventMapper(v1.UserV1SignedOutType, human.SignedOutEventMapper). + RegisterFilterEventMapper(v1.UserV1PasswordChangedType, password.ChangedEventMapper). + RegisterFilterEventMapper(v1.UserV1PasswordCodeAddedType, password.CodeAddedEventMapper). + RegisterFilterEventMapper(v1.UserV1PasswordCodeSentType, password.CodeSentEventMapper). + RegisterFilterEventMapper(v1.UserV1PasswordCheckSucceededType, password.CheckSucceededEventMapper). + RegisterFilterEventMapper(v1.UserV1PasswordCheckFailedType, password.CheckFailedEventMapper). + RegisterFilterEventMapper(v1.UserV1EmailChangedType, email.ChangedEventMapper). + RegisterFilterEventMapper(v1.UserV1EmailVerifiedType, email.VerifiedEventMapper). + RegisterFilterEventMapper(v1.UserV1EmailVerificationFailedType, email.VerificationFailedEventMapper). + RegisterFilterEventMapper(v1.UserV1EmailCodeAddedType, email.CodeAddedEventMapper). + RegisterFilterEventMapper(v1.UserV1EmailCodeSentType, email.CodeSentEventMapper). + RegisterFilterEventMapper(v1.UserV1PhoneChangedType, phone.ChangedEventMapper). + RegisterFilterEventMapper(v1.UserV1PhoneRemovedType, phone.RemovedEventMapper). + RegisterFilterEventMapper(v1.UserV1PhoneVerifiedType, phone.VerifiedEventMapper). + RegisterFilterEventMapper(v1.UserV1PhoneVerificationFailedType, phone.VerificationFailedEventMapper). + RegisterFilterEventMapper(v1.UserV1PhoneCodeAddedType, phone.CodeAddedEventMapper). + RegisterFilterEventMapper(v1.UserV1PhoneCodeSentType, phone.CodeSentEventMapper). + RegisterFilterEventMapper(v1.UserV1ProfileChangedType, profile.ChangedEventMapper). + RegisterFilterEventMapper(v1.UserV1AddressChangedType, address.ChangedEventMapper). + RegisterFilterEventMapper(v1.UserV1MFAInitSkippedType, mfa.InitSkippedEventMapper). + RegisterFilterEventMapper(v1.UserV1MFAOTPAddedType, otp.AddedEventMapper). + RegisterFilterEventMapper(v1.UserV1MFAOTPVerifiedType, otp.VerifiedEventMapper). + RegisterFilterEventMapper(v1.UserV1MFAOTPRemovedType, otp.RemovedEventMapper). + RegisterFilterEventMapper(v1.UserV1MFAOTPCheckSucceededType, otp.CheckSucceededEventMapper). + RegisterFilterEventMapper(v1.UserV1MFAOTPCheckFailedType, otp.CheckFailedEventMapper). + RegisterFilterEventMapper(UserLockedType, LockedEventMapper). + RegisterFilterEventMapper(UserUnlockedType, LockedEventMapper). + RegisterFilterEventMapper(UserDeactivatedType, DeactivatedEventMapper). + RegisterFilterEventMapper(UserReactivatedType, ReactivatedEventMapper). + RegisterFilterEventMapper(UserRemovedType, RemovedEventMapper). + RegisterFilterEventMapper(UserTokenAddedType, TokenAddedEventMapper). + RegisterFilterEventMapper(UserDomainClaimedType, DomainClaimedEventMapper). + RegisterFilterEventMapper(UserDomainClaimedSentType, DomainClaimedEventMapper). + RegisterFilterEventMapper(UserUserNameChangedType, UsernameChangedEventMapper). + RegisterFilterEventMapper(human.HumanAddedType, human.AddedEventMapper). + RegisterFilterEventMapper(human.HumanRegisteredType, human.RegisteredEventMapper). + RegisterFilterEventMapper(human.HumanInitialCodeAddedType, human.InitialCodeAddedEventMapper). + RegisterFilterEventMapper(human.HumanInitialCodeSentType, human.InitialCodeSentEventMapper). + RegisterFilterEventMapper(human.HumanInitializedCheckSucceededType, human.InitializedCheckSucceededEventMapper). + RegisterFilterEventMapper(human.HumanInitializedCheckFailedType, human.InitializedCheckFailedEventMapper). + RegisterFilterEventMapper(human.HumanSignedOutType, human.SignedOutEventMapper). + RegisterFilterEventMapper(password.HumanPasswordChangedType, password.ChangedEventMapper). + RegisterFilterEventMapper(password.HumanPasswordCodeAddedType, password.CodeAddedEventMapper). + RegisterFilterEventMapper(password.HumanPasswordCodeSentType, password.CodeSentEventMapper). + RegisterFilterEventMapper(password.HumanPasswordCheckSucceededType, password.CheckSucceededEventMapper). + RegisterFilterEventMapper(password.HumanPasswordCheckFailedType, password.CheckFailedEventMapper). + RegisterFilterEventMapper(external_idp.HumanExternalIDPAddedType, external_idp.AddedEventMapper). + RegisterFilterEventMapper(external_idp.HumanExternalIDPRemovedType, external_idp.RemovedEventMapper). + RegisterFilterEventMapper(external_idp.HumanExternalIDPCascadeRemovedType, external_idp.CascadeRemovedEventMapper). + RegisterFilterEventMapper(external_idp.HumanExternalLoginCheckSucceededType, external_idp.CheckSucceededEventMapper). + RegisterFilterEventMapper(email.HumanEmailChangedType, email.ChangedEventMapper). + RegisterFilterEventMapper(email.HumanEmailVerifiedType, email.VerifiedEventMapper). + RegisterFilterEventMapper(email.HumanEmailVerificationFailedType, email.VerificationFailedEventMapper). + RegisterFilterEventMapper(email.HumanEmailCodeAddedType, email.CodeAddedEventMapper). + RegisterFilterEventMapper(email.HumanEmailCodeSentType, email.CodeSentEventMapper). + RegisterFilterEventMapper(phone.HumanPhoneChangedType, phone.ChangedEventMapper). + RegisterFilterEventMapper(phone.HumanPhoneRemovedType, phone.RemovedEventMapper). + RegisterFilterEventMapper(phone.HumanPhoneVerifiedType, phone.VerifiedEventMapper). + RegisterFilterEventMapper(phone.HumanPhoneVerificationFailedType, phone.VerificationFailedEventMapper). + RegisterFilterEventMapper(phone.HumanPhoneCodeAddedType, phone.CodeAddedEventMapper). + RegisterFilterEventMapper(phone.HumanPhoneCodeSentType, phone.CodeSentEventMapper). + RegisterFilterEventMapper(profile.HumanProfileChangedType, profile.ChangedEventMapper). + RegisterFilterEventMapper(address.HumanAddressChangedType, address.ChangedEventMapper). + RegisterFilterEventMapper(mfa.HumanMFAInitSkippedType, mfa.InitSkippedEventMapper). + RegisterFilterEventMapper(otp.HumanMFAOTPAddedType, otp.AddedEventMapper). + RegisterFilterEventMapper(otp.HumanMFAOTPVerifiedType, otp.VerifiedEventMapper). + RegisterFilterEventMapper(otp.HumanMFAOTPRemovedType, otp.RemovedEventMapper). + RegisterFilterEventMapper(otp.HumanMFAOTPCheckSucceededType, otp.CheckSucceededEventMapper). + RegisterFilterEventMapper(otp.HumanMFAOTPCheckFailedType, otp.CheckFailedEventMapper). + RegisterFilterEventMapper(web_auth_n.HumanU2FTokenAddedType, web_auth_n.AddedEventMapper). + RegisterFilterEventMapper(web_auth_n.HumanU2FTokenVerifiedType, web_auth_n.VerifiedEventMapper). + RegisterFilterEventMapper(web_auth_n.HumanU2FTokenSignCountChangedType, web_auth_n.SignCountChangedEventMapper). + RegisterFilterEventMapper(web_auth_n.HumanU2FTokenRemovedType, web_auth_n.RemovedEventMapper). + RegisterFilterEventMapper(web_auth_n.HumanU2FTokenBeginLoginType, web_auth_n.BeginLoginEventMapper). + RegisterFilterEventMapper(web_auth_n.HumanU2FTokenCheckSucceededType, web_auth_n.CheckSucceededEventMapper). + RegisterFilterEventMapper(web_auth_n.HumanU2FTokenCheckFailedType, web_auth_n.CheckFailedEventMapper). + RegisterFilterEventMapper(web_auth_n.HumanPasswordlessTokenAddedType, web_auth_n.AddedEventMapper). + RegisterFilterEventMapper(web_auth_n.HumanPasswordlessTokenVerifiedType, web_auth_n.VerifiedEventMapper). + RegisterFilterEventMapper(web_auth_n.HumanPasswordlessTokenSignCountChangedType, web_auth_n.SignCountChangedEventMapper). + RegisterFilterEventMapper(web_auth_n.HumanPasswordlessTokenRemovedType, web_auth_n.RemovedEventMapper). + RegisterFilterEventMapper(web_auth_n.HumanPasswordlessTokenBeginLoginType, web_auth_n.BeginLoginEventMapper). + RegisterFilterEventMapper(web_auth_n.HumanPasswordlessTokenCheckSucceededType, web_auth_n.CheckSucceededEventMapper). + RegisterFilterEventMapper(web_auth_n.HumanPasswordlessTokenCheckFailedType, web_auth_n.CheckFailedEventMapper). + RegisterFilterEventMapper(machine.MachineAddedEventType, machine.AddedEventMapper). + RegisterFilterEventMapper(machine.MachineChangedEventType, machine.ChangedEventMapper). + RegisterFilterEventMapper(keys.MachineKeyAddedEventType, keys.AddedEventMapper). + RegisterFilterEventMapper(keys.MachineKeyRemovedEventType, keys.RemovedEventMapper) +} diff --git a/internal/v2/repository/user/human/address/events.go b/internal/v2/repository/user/human/address/events.go new file mode 100644 index 0000000000..06e38a4bfc --- /dev/null +++ b/internal/v2/repository/user/human/address/events.go @@ -0,0 +1,79 @@ +package address + +import ( + "context" + "encoding/json" + "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/v2" + "github.com/caos/zitadel/internal/eventstore/v2/repository" +) + +const ( + addressEventPrefix = eventstore.EventType("user.human.address.") + HumanAddressChangedType = addressEventPrefix + "changed" +) + +type ChangedEvent struct { + eventstore.BaseEvent `json:"-"` + + Country string `json:"country,omitempty"` + Locality string `json:"locality,omitempty"` + PostalCode string `json:"postalCode,omitempty"` + Region string `json:"region,omitempty"` + StreetAddress string `json:"streetAddress,omitempty"` +} + +func (e *ChangedEvent) CheckPrevious() bool { + return true +} + +func (e *ChangedEvent) Data() interface{} { + return e +} + +func NewChangedEvent( + ctx context.Context, + current *WriteModel, + country, + locality, + postalCode, + region, + streetAddress string, +) *ChangedEvent { + e := &ChangedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + HumanAddressChangedType, + ), + } + + if current.Country != country { + e.Country = country + } + if current.Locality != locality { + e.Locality = locality + } + if current.PostalCode != postalCode { + e.PostalCode = postalCode + } + if current.Region != region { + e.Region = region + } + if current.StreetAddress != streetAddress { + e.StreetAddress = streetAddress + } + + return e +} + +func ChangedEventMapper(event *repository.Event) (eventstore.EventReader, error) { + addressChanged := &ChangedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + } + err := json.Unmarshal(event.Data, addressChanged) + if err != nil { + return nil, errors.ThrowInternal(err, "USER-5M0pd", "unable to unmarshal human address changed") + } + + return addressChanged, nil +} diff --git a/internal/v2/repository/user/human/address/write_model.go b/internal/v2/repository/user/human/address/write_model.go new file mode 100644 index 0000000000..735a13466e --- /dev/null +++ b/internal/v2/repository/user/human/address/write_model.go @@ -0,0 +1,13 @@ +package address + +import "github.com/caos/zitadel/internal/eventstore/v2" + +type WriteModel struct { + eventstore.WriteModel + + Country string + Locality string + PostalCode string + Region string + StreetAddress string +} diff --git a/internal/v2/repository/user/human/email/events.go b/internal/v2/repository/user/human/email/events.go new file mode 100644 index 0000000000..8b56be15d5 --- /dev/null +++ b/internal/v2/repository/user/human/email/events.go @@ -0,0 +1,189 @@ +package email + +import ( + "context" + "encoding/json" + "github.com/caos/zitadel/internal/crypto" + "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/v2" + "github.com/caos/zitadel/internal/eventstore/v2/repository" + "time" +) + +const ( + emailEventPrefix = eventstore.EventType("user.human.email.") + HumanEmailChangedType = emailEventPrefix + "changed" + HumanEmailVerifiedType = emailEventPrefix + "verified" + HumanEmailVerificationFailedType = emailEventPrefix + "verification.failed" + HumanEmailCodeAddedType = emailEventPrefix + "code.added" + HumanEmailCodeSentType = emailEventPrefix + "code.sent" +) + +type ChangedEvent struct { + eventstore.BaseEvent `json:"-"` + + EmailAddress string `json:"email,omitempty"` +} + +func (e *ChangedEvent) CheckPrevious() bool { + return true +} + +func (e *ChangedEvent) Data() interface{} { + return e +} + +func NewChangedEvent( + ctx context.Context, + current *WriteModel, + emailAddress string, +) *ChangedEvent { + e := &ChangedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + HumanEmailChangedType, + ), + } + if current.Email != emailAddress { + e.EmailAddress = emailAddress + } + return e +} + +func ChangedEventMapper(event *repository.Event) (eventstore.EventReader, error) { + emailChangedEvent := &ChangedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + } + err := json.Unmarshal(event.Data, emailChangedEvent) + if err != nil { + return nil, errors.ThrowInternal(err, "USER-4M0sd", "unable to unmarshal human password changed") + } + + return emailChangedEvent, nil +} + +type VerifiedEvent struct { + eventstore.BaseEvent `json:"-"` + + IsEmailVerified bool `json:"-"` +} + +func (e *VerifiedEvent) CheckPrevious() bool { + return true +} + +func (e *VerifiedEvent) Data() interface{} { + return nil +} + +func NewVerifiedEvent(ctx context.Context) *VerifiedEvent { + return &VerifiedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + HumanEmailVerifiedType, + ), + } +} + +func VerifiedEventMapper(event *repository.Event) (eventstore.EventReader, error) { + emailVerified := &VerifiedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + IsEmailVerified: true, + } + return emailVerified, nil +} + +type VerificationFailedEvent struct { + eventstore.BaseEvent `json:"-"` +} + +func (e *VerificationFailedEvent) CheckPrevious() bool { + return false +} + +func (e *VerificationFailedEvent) Data() interface{} { + return nil +} + +func NewVerificationFailedEvent(ctx context.Context) *VerificationFailedEvent { + return &VerificationFailedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + HumanEmailVerificationFailedType, + ), + } +} + +func VerificationFailedEventMapper(event *repository.Event) (eventstore.EventReader, error) { + return &VerificationFailedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + }, nil +} + +type CodeAddedEvent struct { + eventstore.BaseEvent `json:"-"` + + Code *crypto.CryptoValue `json:"code,omitempty"` + Expiry time.Duration `json:"expiry,omitempty"` +} + +func (e *CodeAddedEvent) CheckPrevious() bool { + return true +} + +func (e *CodeAddedEvent) Data() interface{} { + return e +} + +func NewCodeAddedEvent( + ctx context.Context, + code *crypto.CryptoValue, + expiry time.Duration) *CodeAddedEvent { + return &CodeAddedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + HumanEmailCodeAddedType, + ), + Code: code, + Expiry: expiry, + } +} + +func CodeAddedEventMapper(event *repository.Event) (eventstore.EventReader, error) { + codeAdded := &CodeAddedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + } + err := json.Unmarshal(event.Data, codeAdded) + if err != nil { + return nil, errors.ThrowInternal(err, "USER-3M0sd", "unable to unmarshal human email code added") + } + + return codeAdded, nil +} + +type CodeSentEvent struct { + eventstore.BaseEvent `json:"-"` +} + +func (e *CodeSentEvent) CheckPrevious() bool { + return true +} + +func (e *CodeSentEvent) Data() interface{} { + return nil +} + +func NewCodeSentEvent(ctx context.Context) *CodeSentEvent { + return &CodeSentEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + HumanEmailCodeSentType, + ), + } +} + +func CodeSentEventMapper(event *repository.Event) (eventstore.EventReader, error) { + return &CodeSentEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + }, nil +} diff --git a/internal/v2/repository/user/human/email/write_model.go b/internal/v2/repository/user/human/email/write_model.go new file mode 100644 index 0000000000..3cf04143fc --- /dev/null +++ b/internal/v2/repository/user/human/email/write_model.go @@ -0,0 +1,9 @@ +package email + +import "github.com/caos/zitadel/internal/eventstore/v2" + +type WriteModel struct { + eventstore.WriteModel + + Email string +} diff --git a/internal/v2/repository/user/human/events.go b/internal/v2/repository/user/human/events.go new file mode 100644 index 0000000000..f2f4453dd6 --- /dev/null +++ b/internal/v2/repository/user/human/events.go @@ -0,0 +1,336 @@ +package human + +import ( + "context" + "encoding/json" + "github.com/caos/zitadel/internal/crypto" + "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/v2" + "github.com/caos/zitadel/internal/eventstore/v2/repository" + "golang.org/x/text/language" + "time" +) + +const ( + humanEventPrefix = eventstore.EventType("user.human.") + HumanAddedType = humanEventPrefix + "added" + HumanRegisteredType = humanEventPrefix + "selfregistered" + HumanInitialCodeAddedType = humanEventPrefix + "initialization.code.added" + HumanInitialCodeSentType = humanEventPrefix + "initialization.code.sent" + HumanInitializedCheckSucceededType = humanEventPrefix + "initialization.check.succeeded" + HumanInitializedCheckFailedType = humanEventPrefix + "initialization.check.failed" + HumanSignedOutType = humanEventPrefix + "signed.out" +) + +type AddedEvent struct { + eventstore.BaseEvent `json:"-"` + + UserName string `json:"userName"` + + FirstName string `json:"firstName,omitempty"` + LastName string `json:"lastName,omitempty"` + NickName string `json:"nickName,omitempty"` + DisplayName string `json:"displayName,omitempty"` + PreferredLanguage language.Tag `json:"preferredLanguage,omitempty"` + Gender Gender `json:"gender,omitempty"` + + EmailAddress string `json:"email,omitempty"` + + PhoneNumber string `json:"phone,omitempty"` + + Country string `json:"country,omitempty"` + Locality string `json:"locality,omitempty"` + PostalCode string `json:"postalCode,omitempty"` + Region string `json:"region,omitempty"` + StreetAddress string `json:"streetAddress,omitempty"` +} + +func (e *AddedEvent) CheckPrevious() bool { + return false +} + +func (e *AddedEvent) Data() interface{} { + return e +} + +func NewAddedEvent( + ctx context.Context, + userName, + firstName, + lastName, + nickName, + displayName string, + preferredLanguage language.Tag, + gender Gender, + emailAddress, + phoneNumber, + country, + locality, + postalCode, + region, + streetAddress string, +) *AddedEvent { + return &AddedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + HumanAddedType, + ), + UserName: userName, + FirstName: firstName, + LastName: lastName, + NickName: nickName, + DisplayName: displayName, + PreferredLanguage: preferredLanguage, + Gender: gender, + EmailAddress: emailAddress, + PhoneNumber: phoneNumber, + Country: country, + Locality: locality, + PostalCode: postalCode, + Region: region, + StreetAddress: streetAddress, + } +} + +func AddedEventMapper(event *repository.Event) (eventstore.EventReader, error) { + humanAdded := &AddedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + } + err := json.Unmarshal(event.Data, humanAdded) + if err != nil { + return nil, errors.ThrowInternal(err, "USER-5Gm9s", "unable to unmarshal human added") + } + + return humanAdded, nil +} + +type RegisteredEvent struct { + eventstore.BaseEvent `json:"-"` + + UserName string `json:"userName"` + + FirstName string `json:"firstName,omitempty"` + LastName string `json:"lastName,omitempty"` + NickName string `json:"nickName,omitempty"` + DisplayName string `json:"displayName,omitempty"` + PreferredLanguage language.Tag `json:"preferredLanguage,omitempty"` + Gender int32 `json:"gender,omitempty"` + + EmailAddress string `json:"email,omitempty"` + + PhoneNumber string `json:"phone,omitempty"` + + Country string `json:"country,omitempty"` + Locality string `json:"locality,omitempty"` + PostalCode string `json:"postalCode,omitempty"` + Region string `json:"region,omitempty"` + StreetAddress string `json:"streetAddress,omitempty"` +} + +func (e *RegisteredEvent) CheckPrevious() bool { + return false +} + +func (e *RegisteredEvent) Data() interface{} { + return e +} + +func NewRegisteredEvent( + ctx context.Context, + userName, + firstName, + lastName, + nickName, + displayName string, + preferredLanguage language.Tag, + gender int32, + emailAddress, + phoneNumber, + country, + locality, + postalCode, + region, + streetAddress string, +) *RegisteredEvent { + return &RegisteredEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + HumanRegisteredType, + ), + UserName: userName, + FirstName: firstName, + LastName: lastName, + NickName: nickName, + DisplayName: displayName, + PreferredLanguage: preferredLanguage, + Gender: gender, + EmailAddress: emailAddress, + PhoneNumber: phoneNumber, + Country: country, + Locality: locality, + PostalCode: postalCode, + Region: region, + StreetAddress: streetAddress, + } +} + +func RegisteredEventMapper(event *repository.Event) (eventstore.EventReader, error) { + humanRegistered := &RegisteredEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + } + err := json.Unmarshal(event.Data, humanRegistered) + if err != nil { + return nil, errors.ThrowInternal(err, "USER-3Vm9s", "unable to unmarshal human registered") + } + + return humanRegistered, nil +} + +type InitialCodeAddedEvent struct { + eventstore.BaseEvent `json:"-"` + Code *crypto.CryptoValue `json:"code,omitempty"` + Expiry time.Duration `json:"expiry,omitempty"` +} + +func (e *InitialCodeAddedEvent) CheckPrevious() bool { + return true +} + +func (e *InitialCodeAddedEvent) Data() interface{} { + return e +} + +func NewInitialCodeAddedEvent( + ctx context.Context, + code *crypto.CryptoValue, + expiry time.Duration, +) *InitialCodeAddedEvent { + return &InitialCodeAddedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + HumanInitialCodeAddedType, + ), + Code: code, + Expiry: expiry, + } +} + +func InitialCodeAddedEventMapper(event *repository.Event) (eventstore.EventReader, error) { + humanRegistered := &InitialCodeAddedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + } + err := json.Unmarshal(event.Data, humanRegistered) + if err != nil { + return nil, errors.ThrowInternal(err, "USER-bM9se", "unable to unmarshal human initial code added") + } + + return humanRegistered, nil +} + +type InitialCodeSentEvent struct { + eventstore.BaseEvent `json:"-"` +} + +func (e *InitialCodeSentEvent) CheckPrevious() bool { + return false +} + +func (e *InitialCodeSentEvent) Data() interface{} { + return nil +} + +func NewInitialCodeSentEvent(ctx context.Context) *InitialCodeSentEvent { + return &InitialCodeSentEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + HumanInitialCodeSentType, + ), + } +} + +func InitialCodeSentEventMapper(event *repository.Event) (eventstore.EventReader, error) { + return &InitialCodeSentEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + }, nil +} + +type InitializedCheckSucceededEvent struct { + eventstore.BaseEvent `json:"-"` +} + +func (e *InitializedCheckSucceededEvent) CheckPrevious() bool { + return false +} + +func (e *InitializedCheckSucceededEvent) Data() interface{} { + return nil +} + +func NewInitializedCheckSucceededEvent(ctx context.Context) *InitializedCheckSucceededEvent { + return &InitializedCheckSucceededEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + HumanInitializedCheckSucceededType, + ), + } +} + +func InitializedCheckSucceededEventMapper(event *repository.Event) (eventstore.EventReader, error) { + return &InitializedCheckSucceededEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + }, nil +} + +type InitializedCheckFailedEvent struct { + eventstore.BaseEvent `json:"-"` +} + +func (e *InitializedCheckFailedEvent) CheckPrevious() bool { + return false +} + +func (e *InitializedCheckFailedEvent) Data() interface{} { + return nil +} + +func NewInitializedCheckFailedEvent(ctx context.Context) *InitializedCheckFailedEvent { + return &InitializedCheckFailedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + HumanInitializedCheckFailedType, + ), + } +} + +func InitializedCheckFailedEventMapper(event *repository.Event) (eventstore.EventReader, error) { + return &InitializedCheckFailedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + }, nil +} + +type SignedOutEvent struct { + eventstore.BaseEvent `json:"-"` +} + +func (e *SignedOutEvent) CheckPrevious() bool { + return false +} + +func (e *SignedOutEvent) Data() interface{} { + return nil +} + +func NewSignedOutEvent(ctx context.Context) *SignedOutEvent { + return &SignedOutEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + HumanSignedOutType, + ), + } +} + +func SignedOutEventMapper(event *repository.Event) (eventstore.EventReader, error) { + return &SignedOutEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + }, nil +} diff --git a/internal/v2/repository/user/human/external_idp/events.go b/internal/v2/repository/user/human/external_idp/events.go new file mode 100644 index 0000000000..8d5a50172c --- /dev/null +++ b/internal/v2/repository/user/human/external_idp/events.go @@ -0,0 +1,206 @@ +package external_idp + +import ( + "context" + "encoding/json" + "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/v2" + "github.com/caos/zitadel/internal/eventstore/v2/repository" +) + +const ( + externalIDPEventPrefix = eventstore.EventType("user.human.externalidp.") + externalLoginEventPrefix = eventstore.EventType("user.human.externallogin.") + + //TODO: Handle unique Aggregate + HumanExternalIDPReservedType = externalIDPEventPrefix + "reserved" + HumanExternalIDPReleasedType = externalIDPEventPrefix + "released" + + HumanExternalIDPAddedType = externalIDPEventPrefix + "added" + HumanExternalIDPRemovedType = externalIDPEventPrefix + "removed" + HumanExternalIDPCascadeRemovedType = externalIDPEventPrefix + "cascade.removed" + + HumanExternalLoginCheckSucceededType = externalLoginEventPrefix + "check.succeeded" +) + +type ReservedEvent struct { + eventstore.BaseEvent `json:"-"` +} + +func (e *ReservedEvent) CheckPrevious() bool { + return true +} + +func (e *ReservedEvent) Data() interface{} { + return nil +} + +func NewReservedEvent(ctx context.Context) *ReservedEvent { + return &ReservedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + HumanExternalIDPReservedType, + ), + } +} + +type ReleasedEvent struct { + eventstore.BaseEvent `json:"-"` +} + +func (e *ReleasedEvent) CheckPrevious() bool { + return true +} + +func (e *ReleasedEvent) Data() interface{} { + return nil +} + +func NewReleasedEvent(ctx context.Context) *ReleasedEvent { + return &ReleasedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + HumanExternalIDPReleasedType, + ), + } +} + +type AddedEvent struct { + eventstore.BaseEvent `json:"-"` + + IDPConfigID string `json:"idpConfigId,omitempty"` + DisplayName string `json:"displayName,omitempty"` +} + +func (e *AddedEvent) CheckPrevious() bool { + return true +} + +func (e *AddedEvent) Data() interface{} { + return e +} + +func NewAddedEvent(ctx context.Context, idpConfigID, displayName string) *AddedEvent { + return &AddedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + HumanExternalIDPAddedType, + ), + IDPConfigID: idpConfigID, + DisplayName: displayName, + } +} + +func AddedEventMapper(event *repository.Event) (eventstore.EventReader, error) { + e := &AddedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + } + + err := json.Unmarshal(event.Data, e) + if err != nil { + return nil, errors.ThrowInternal(err, "USER-6M9sd", "unable to unmarshal user external idp added") + } + + return e, nil +} + +type RemovedEvent struct { + eventstore.BaseEvent `json:"-"` + + IDPConfigID string `json:"idpConfigId"` +} + +func (e *RemovedEvent) CheckPrevious() bool { + return true +} + +func (e *RemovedEvent) Data() interface{} { + return e +} + +func NewRemovedEvent(ctx context.Context, idpConfigID string) *RemovedEvent { + return &RemovedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + HumanExternalIDPRemovedType, + ), + IDPConfigID: idpConfigID, + } +} + +func RemovedEventMapper(event *repository.Event) (eventstore.EventReader, error) { + e := &RemovedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + } + + err := json.Unmarshal(event.Data, e) + if err != nil { + return nil, errors.ThrowInternal(err, "USER-5Gm9s", "unable to unmarshal user external idp removed") + } + + return e, nil +} + +type CascadeRemovedEvent struct { + eventstore.BaseEvent `json:"-"` + + IDPConfigID string `json:"idpConfigId"` +} + +func (e *CascadeRemovedEvent) CheckPrevious() bool { + return false +} + +func (e *CascadeRemovedEvent) Data() interface{} { + return e +} + +func NewCascadeRemovedEvent(ctx context.Context, idpConfigID string) *CascadeRemovedEvent { + return &CascadeRemovedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + HumanExternalIDPCascadeRemovedType, + ), + IDPConfigID: idpConfigID, + } +} + +func CascadeRemovedEventMapper(event *repository.Event) (eventstore.EventReader, error) { + e := &CascadeRemovedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + } + + err := json.Unmarshal(event.Data, e) + if err != nil { + return nil, errors.ThrowInternal(err, "USER-2M0sd", "unable to unmarshal user external idp cascade removed") + } + + return e, nil +} + +type CheckSucceededEvent struct { + eventstore.BaseEvent `json:"-"` +} + +func (e *CheckSucceededEvent) CheckPrevious() bool { + return false +} + +func (e *CheckSucceededEvent) Data() interface{} { + return nil +} + +func NewCheckSucceededEvent(ctx context.Context) *CheckSucceededEvent { + return &CheckSucceededEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + HumanExternalLoginCheckSucceededType, + ), + } +} + +func CheckSucceededEventMapper(event *repository.Event) (eventstore.EventReader, error) { + return &CheckSucceededEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + }, nil +} diff --git a/internal/v2/repository/user/human/human.go b/internal/v2/repository/user/human/human.go new file mode 100644 index 0000000000..548d2a45e5 --- /dev/null +++ b/internal/v2/repository/user/human/human.go @@ -0,0 +1,16 @@ +package human + +type Gender int32 + +const ( + GenderUnspecified Gender = iota + GenderFemale + GenderMale + GenderDiverse + + genderCount +) + +func (f Gender) Valid() bool { + return f >= 0 && f < genderCount +} diff --git a/internal/v2/repository/user/human/mfa/events.go b/internal/v2/repository/user/human/mfa/events.go new file mode 100644 index 0000000000..3e0531555a --- /dev/null +++ b/internal/v2/repository/user/human/mfa/events.go @@ -0,0 +1,39 @@ +package mfa + +import ( + "context" + "github.com/caos/zitadel/internal/eventstore/v2" + "github.com/caos/zitadel/internal/eventstore/v2/repository" +) + +const ( + mfaEventPrefix = eventstore.EventType("user.human.mfa.") + HumanMFAInitSkippedType = mfaEventPrefix + "init.skiped" +) + +type InitSkippedEvent struct { + eventstore.BaseEvent `json:"-"` +} + +func (e *InitSkippedEvent) CheckPrevious() bool { + return true +} + +func (e *InitSkippedEvent) Data() interface{} { + return e +} + +func NewInitSkippedEvent(ctx context.Context) *InitSkippedEvent { + return &InitSkippedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + HumanMFAInitSkippedType, + ), + } +} + +func InitSkippedEventMapper(event *repository.Event) (eventstore.EventReader, error) { + return &InitSkippedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + }, nil +} diff --git a/internal/v2/repository/user/human/mfa/mfa.go b/internal/v2/repository/user/human/mfa/mfa.go new file mode 100644 index 0000000000..19df406bd6 --- /dev/null +++ b/internal/v2/repository/user/human/mfa/mfa.go @@ -0,0 +1,15 @@ +package mfa + +type State int32 + +const ( + StateUnspecified State = iota + StateNotReady + StateReady + + stateCount +) + +func (f State) Valid() bool { + return f >= 0 && f < stateCount +} diff --git a/internal/v2/repository/user/human/mfa/otp/events.go b/internal/v2/repository/user/human/mfa/otp/events.go new file mode 100644 index 0000000000..5b8f79cd30 --- /dev/null +++ b/internal/v2/repository/user/human/mfa/otp/events.go @@ -0,0 +1,168 @@ +package otp + +import ( + "context" + "encoding/json" + "github.com/caos/zitadel/internal/crypto" + "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/v2" + "github.com/caos/zitadel/internal/eventstore/v2/repository" + "github.com/caos/zitadel/internal/v2/repository/user/human/mfa" +) + +const ( + otpEventPrefix = eventstore.EventType("user.human.mfa.otp.") + HumanMFAOTPAddedType = otpEventPrefix + "added" + HumanMFAOTPVerifiedType = otpEventPrefix + "verified" + HumanMFAOTPRemovedType = otpEventPrefix + "removed" + HumanMFAOTPCheckSucceededType = otpEventPrefix + "check.succeeded" + HumanMFAOTPCheckFailedType = otpEventPrefix + "check.failed" +) + +type AddedEvent struct { + eventstore.BaseEvent `json:"-"` + + Secret *crypto.CryptoValue `json:"otpSecret,omitempty"` + State mfa.State `json:"-"` +} + +func (e *AddedEvent) CheckPrevious() bool { + return true +} + +func (e *AddedEvent) Data() interface{} { + return e +} + +func NewAddedEvent(ctx context.Context, + secret *crypto.CryptoValue) *AddedEvent { + return &AddedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + HumanMFAOTPAddedType, + ), + Secret: secret, + } +} + +func AddedEventMapper(event *repository.Event) (eventstore.EventReader, error) { + otpAdded := &AddedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + State: mfa.StateNotReady, + } + err := json.Unmarshal(event.Data, otpAdded) + if err != nil { + return nil, errors.ThrowInternal(err, "USER-Ns9df", "unable to unmarshal human otp added") + } + return otpAdded, nil +} + +type VerifiedEvent struct { + eventstore.BaseEvent `json:"-"` + State mfa.State `json:"-"` +} + +func (e *VerifiedEvent) CheckPrevious() bool { + return true +} + +func (e *VerifiedEvent) Data() interface{} { + return nil +} + +func NewVerifiedEvent(ctx context.Context) *VerifiedEvent { + return &VerifiedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + HumanMFAOTPVerifiedType, + ), + } +} + +func VerifiedEventMapper(event *repository.Event) (eventstore.EventReader, error) { + return &VerifiedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + State: mfa.StateReady, + }, nil +} + +type RemovedEvent struct { + eventstore.BaseEvent `json:"-"` +} + +func (e *RemovedEvent) CheckPrevious() bool { + return true +} + +func (e *RemovedEvent) Data() interface{} { + return nil +} + +func NewRemovedEvent(ctx context.Context) *RemovedEvent { + return &RemovedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + HumanMFAOTPRemovedType, + ), + } +} + +func RemovedEventMapper(event *repository.Event) (eventstore.EventReader, error) { + return &RemovedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + }, nil +} + +type CheckSucceededEvent struct { + eventstore.BaseEvent `json:"-"` +} + +func (e *CheckSucceededEvent) CheckPrevious() bool { + return false +} + +func (e *CheckSucceededEvent) Data() interface{} { + return nil +} + +func NewCheckSucceededEvent(ctx context.Context) *CheckSucceededEvent { + return &CheckSucceededEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + HumanMFAOTPCheckSucceededType, + ), + } +} + +func CheckSucceededEventMapper(event *repository.Event) (eventstore.EventReader, error) { + return &CheckSucceededEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + }, nil +} + +type CheckFailedEvent struct { + eventstore.BaseEvent `json:"-"` +} + +func (e *CheckFailedEvent) CheckPrevious() bool { + return false +} + +func (e *CheckFailedEvent) Data() interface{} { + return nil +} + +func NewCheckFailedEvent(ctx context.Context) *CheckFailedEvent { + return &CheckFailedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + HumanMFAOTPCheckFailedType, + ), + } +} + +func CheckFailedEventMapper(event *repository.Event) (eventstore.EventReader, error) { + return &CheckFailedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + }, nil +} diff --git a/internal/v2/repository/user/human/mfa/web_auth_n/events.go b/internal/v2/repository/user/human/mfa/web_auth_n/events.go new file mode 100644 index 0000000000..e900edace2 --- /dev/null +++ b/internal/v2/repository/user/human/mfa/web_auth_n/events.go @@ -0,0 +1,426 @@ +package web_auth_n + +import ( + "context" + "encoding/json" + "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/v2" + "github.com/caos/zitadel/internal/eventstore/v2/repository" + "github.com/caos/zitadel/internal/v2/repository/user/human/mfa" +) + +const ( + u2fEventPrefix = eventstore.EventType("user.human.mfa.u2f.token.") + HumanU2FTokenAddedType = u2fEventPrefix + "added" + HumanU2FTokenVerifiedType = u2fEventPrefix + "verified" + HumanU2FTokenSignCountChangedType = u2fEventPrefix + "signcount.changed" + HumanU2FTokenRemovedType = u2fEventPrefix + "removed" + HumanU2FTokenBeginLoginType = u2fEventPrefix + "begin.login" + HumanU2FTokenCheckSucceededType = u2fEventPrefix + "check.succeeded" + HumanU2FTokenCheckFailedType = u2fEventPrefix + "check.failed" + + passwordlessEventPrefix = eventstore.EventType("user.human.mfa.passwordless.token.") + HumanPasswordlessTokenAddedType = passwordlessEventPrefix + "added" + HumanPasswordlessTokenVerifiedType = passwordlessEventPrefix + "verified" + HumanPasswordlessTokenSignCountChangedType = passwordlessEventPrefix + "signcount.changed" + HumanPasswordlessTokenRemovedType = passwordlessEventPrefix + "removed" + HumanPasswordlessTokenBeginLoginType = passwordlessEventPrefix + "begin.login" + HumanPasswordlessTokenCheckSucceededType = passwordlessEventPrefix + "check.succeeded" + HumanPasswordlessTokenCheckFailedType = passwordlessEventPrefix + "check.failed" +) + +type AddedEvent struct { + eventstore.BaseEvent `json:"-"` + + WebAuthNTokenID string `json:"webAuthNTokenId"` + Challenge string `json:"challenge"` + State mfa.State `json:"-"` +} + +func (e *AddedEvent) CheckPrevious() bool { + return true +} + +func (e *AddedEvent) Data() interface{} { + return e +} + +func NewU2FAddedEvent( + ctx context.Context, + webAuthNTokenID, + challenge string, +) *AddedEvent { + return &AddedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + HumanU2FTokenAddedType, + ), + WebAuthNTokenID: webAuthNTokenID, + Challenge: challenge, + } +} + +func NewPasswordlessAddedEvent( + ctx context.Context, + webAuthNTokenID, + challenge string, +) *AddedEvent { + return &AddedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + HumanPasswordlessTokenAddedType, + ), + WebAuthNTokenID: webAuthNTokenID, + Challenge: challenge, + } +} + +func AddedEventMapper(event *repository.Event) (eventstore.EventReader, error) { + webAuthNAdded := &AddedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + State: mfa.StateNotReady, + } + err := json.Unmarshal(event.Data, webAuthNAdded) + if err != nil { + return nil, errors.ThrowInternal(err, "USER-tB8sf", "unable to unmarshal human webAuthN added") + } + return webAuthNAdded, nil +} + +type VerifiedEvent struct { + eventstore.BaseEvent `json:"-"` + + WebAuthNTokenID string `json:"webAuthNTokenId"` + KeyID []byte `json:"keyId"` + PublicKey []byte `json:"publicKey"` + AttestationType string `json:"attestationType"` + AAGUID []byte `json:"aaguid"` + SignCount uint32 `json:"signCount"` + WebAuthNTokenName string `json:"webAuthNTokenName"` + State mfa.State `json:"-"` +} + +func (e *VerifiedEvent) CheckPrevious() bool { + return true +} + +func (e *VerifiedEvent) Data() interface{} { + return e +} + +func NewU2FVerifiedEvent( + ctx context.Context, + webAuthNTokenID, + webAuthNTokenName, + attestationType string, + keyID, + publicKey, + aaguid []byte, + signCount uint32, +) *VerifiedEvent { + return &VerifiedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + HumanU2FTokenVerifiedType, + ), + WebAuthNTokenID: webAuthNTokenID, + KeyID: keyID, + PublicKey: publicKey, + AttestationType: attestationType, + AAGUID: aaguid, + SignCount: signCount, + WebAuthNTokenName: webAuthNTokenName, + } +} + +func NewPasswordlessVerifiedEvent( + ctx context.Context, + webAuthNTokenID, + webAuthNTokenName, + attestationType string, + keyID, + publicKey, + aaguid []byte, + signCount uint32, +) *VerifiedEvent { + return &VerifiedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + HumanPasswordlessTokenVerifiedType, + ), + WebAuthNTokenID: webAuthNTokenID, + KeyID: keyID, + PublicKey: publicKey, + AttestationType: attestationType, + AAGUID: aaguid, + SignCount: signCount, + WebAuthNTokenName: webAuthNTokenName, + } +} + +func VerifiedEventMapper(event *repository.Event) (eventstore.EventReader, error) { + webauthNVerified := &VerifiedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + State: mfa.StateReady, + } + err := json.Unmarshal(event.Data, webauthNVerified) + if err != nil { + return nil, errors.ThrowInternal(err, "USER-B0zDs", "unable to unmarshal human webAuthN verified") + } + return webauthNVerified, nil +} + +type SignCountChangedEvent struct { + eventstore.BaseEvent `json:"-"` + + WebAuthNTokenID string `json:"webAuthNTokenId"` + SignCount uint32 `json:"signCount"` + State mfa.State `json:"-"` +} + +func (e *SignCountChangedEvent) CheckPrevious() bool { + return true +} + +func (e *SignCountChangedEvent) Data() interface{} { + return e +} + +func NewU2FSignCountChangedEvent( + ctx context.Context, + webAuthNTokenID string, + signCount uint32, +) *SignCountChangedEvent { + return &SignCountChangedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + HumanU2FTokenSignCountChangedType, + ), + WebAuthNTokenID: webAuthNTokenID, + SignCount: signCount, + } +} + +func NewPasswordlessSignCountChangedEvent( + ctx context.Context, + webAuthNTokenID string, + signCount uint32, +) *SignCountChangedEvent { + return &SignCountChangedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + HumanPasswordlessTokenSignCountChangedType, + ), + WebAuthNTokenID: webAuthNTokenID, + SignCount: signCount, + } +} + +func SignCountChangedEventMapper(event *repository.Event) (eventstore.EventReader, error) { + webauthNVerified := &SignCountChangedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + } + err := json.Unmarshal(event.Data, webauthNVerified) + if err != nil { + return nil, errors.ThrowInternal(err, "USER-5Gm0s", "unable to unmarshal human webAuthN sign count") + } + return webauthNVerified, nil +} + +type RemovedEvent struct { + eventstore.BaseEvent `json:"-"` + + WebAuthNTokenID string `json:"webAuthNTokenId"` + State mfa.State `json:"-"` +} + +func (e *RemovedEvent) CheckPrevious() bool { + return true +} + +func (e *RemovedEvent) Data() interface{} { + return e +} + +func NewU2FRemovedEvent( + ctx context.Context, + webAuthNTokenID string, +) *RemovedEvent { + return &RemovedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + HumanU2FTokenRemovedType, + ), + WebAuthNTokenID: webAuthNTokenID, + } +} + +func NewPasswordlessRemovedEvent( + ctx context.Context, + webAuthNTokenID string, +) *RemovedEvent { + return &RemovedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + HumanPasswordlessTokenRemovedType, + ), + WebAuthNTokenID: webAuthNTokenID, + } +} + +func RemovedEventMapper(event *repository.Event) (eventstore.EventReader, error) { + webauthNVerified := &RemovedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + } + err := json.Unmarshal(event.Data, webauthNVerified) + if err != nil { + return nil, errors.ThrowInternal(err, "USER-gM9sd", "unable to unmarshal human webAuthN token removed") + } + return webauthNVerified, nil +} + +type BeginLoginEvent struct { + eventstore.BaseEvent `json:"-"` + + WebAuthNTokenID string `json:"webAuthNTokenId"` + Challenge string `json:"challenge"` + //TODO: Handle Auth Req?? + //*AuthRequest +} + +func (e *BeginLoginEvent) CheckPrevious() bool { + return true +} + +func (e *BeginLoginEvent) Data() interface{} { + return e +} + +func NewU2FBeginLoginEvent( + ctx context.Context, + webAuthNTokenID, + challenge string, +) *BeginLoginEvent { + return &BeginLoginEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + HumanU2FTokenRemovedType, + ), + WebAuthNTokenID: webAuthNTokenID, + Challenge: challenge, + } +} + +func NewPasswordlessBeginLoginEvent( + ctx context.Context, + webAuthNTokenID, + challenge string, +) *BeginLoginEvent { + return &BeginLoginEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + HumanPasswordlessTokenRemovedType, + ), + WebAuthNTokenID: webAuthNTokenID, + Challenge: challenge, + } +} + +func BeginLoginEventMapper(event *repository.Event) (eventstore.EventReader, error) { + webAuthNAdded := &BeginLoginEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + } + err := json.Unmarshal(event.Data, webAuthNAdded) + if err != nil { + return nil, errors.ThrowInternal(err, "USER-rMb8x", "unable to unmarshal human webAuthN begin login") + } + return webAuthNAdded, nil +} + +type CheckSucceededEvent struct { + eventstore.BaseEvent `json:"-"` + + //TODO: Handle Auth Req?? + //*AuthRequest +} + +func (e *CheckSucceededEvent) CheckPrevious() bool { + return true +} + +func (e *CheckSucceededEvent) Data() interface{} { + return e +} + +func NewU2FCheckSucceededEvent(ctx context.Context) *CheckSucceededEvent { + return &CheckSucceededEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + HumanU2FTokenCheckSucceededType, + ), + } +} + +func NewPasswordlessCheckSucceededEvent(ctx context.Context) *CheckSucceededEvent { + return &CheckSucceededEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + HumanPasswordlessTokenCheckSucceededType, + ), + } +} + +func CheckSucceededEventMapper(event *repository.Event) (eventstore.EventReader, error) { + webAuthNAdded := &CheckSucceededEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + } + err := json.Unmarshal(event.Data, webAuthNAdded) + if err != nil { + return nil, errors.ThrowInternal(err, "USER-2M0fg", "unable to unmarshal human webAuthN check succeeded") + } + return webAuthNAdded, nil +} + +type CheckFailedEvent struct { + eventstore.BaseEvent `json:"-"` + + //TODO: Handle Auth Req?? + //*AuthRequest +} + +func (e *CheckFailedEvent) CheckPrevious() bool { + return true +} + +func (e *CheckFailedEvent) Data() interface{} { + return e +} + +func NewU2FCheckFailedEvent(ctx context.Context) *CheckFailedEvent { + return &CheckFailedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + HumanU2FTokenCheckFailedType, + ), + } +} + +func NewPasswordlessCheckFailedEvent(ctx context.Context) *CheckFailedEvent { + return &CheckFailedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + HumanPasswordlessTokenCheckFailedType, + ), + } +} + +func CheckFailedEventMapper(event *repository.Event) (eventstore.EventReader, error) { + webAuthNAdded := &CheckFailedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + } + err := json.Unmarshal(event.Data, webAuthNAdded) + if err != nil { + return nil, errors.ThrowInternal(err, "USER-O0dse", "unable to unmarshal human webAuthN check failed") + } + return webAuthNAdded, nil +} diff --git a/internal/v2/repository/user/human/notification.go b/internal/v2/repository/user/human/notification.go new file mode 100644 index 0000000000..6ee815e0c7 --- /dev/null +++ b/internal/v2/repository/user/human/notification.go @@ -0,0 +1,8 @@ +package human + +type NotificationType int32 + +const ( + NotificationTypeEmail NotificationType = iota + NotificationTypeSms +) diff --git a/internal/v2/repository/user/human/password/events.go b/internal/v2/repository/user/human/password/events.go new file mode 100644 index 0000000000..54ffa7f0df --- /dev/null +++ b/internal/v2/repository/user/human/password/events.go @@ -0,0 +1,189 @@ +package password + +import ( + "context" + "encoding/json" + "github.com/caos/zitadel/internal/crypto" + "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/v2" + "github.com/caos/zitadel/internal/eventstore/v2/repository" + "github.com/caos/zitadel/internal/v2/repository/user/human" + "time" +) + +const ( + passwordEventPrefix = eventstore.EventType("user.human.password.") + HumanPasswordChangedType = passwordEventPrefix + "changed" + HumanPasswordCodeAddedType = passwordEventPrefix + "code.added" + HumanPasswordCodeSentType = passwordEventPrefix + "code.sent" + HumanPasswordCheckSucceededType = passwordEventPrefix + "check.succeeded" + HumanPasswordCheckFailedType = passwordEventPrefix + "check.failed" +) + +type ChangedEvent struct { + eventstore.BaseEvent `json:"-"` + + Secret *crypto.CryptoValue `json:"secret,omitempty"` + ChangeRequired bool `json:"changeRequired,omitempty"` +} + +func (e *ChangedEvent) CheckPrevious() bool { + return false +} + +func (e *ChangedEvent) Data() interface{} { + return e +} + +func NewChangedEvent( + ctx context.Context, + secret *crypto.CryptoValue, + changeRequired bool, +) *ChangedEvent { + return &ChangedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + HumanPasswordChangedType, + ), + Secret: secret, + ChangeRequired: changeRequired, + } +} + +func ChangedEventMapper(event *repository.Event) (eventstore.EventReader, error) { + humanAdded := &ChangedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + } + err := json.Unmarshal(event.Data, humanAdded) + if err != nil { + return nil, errors.ThrowInternal(err, "USER-4M0sd", "unable to unmarshal human password changed") + } + + return humanAdded, nil +} + +type CodeAddedEvent struct { + eventstore.BaseEvent `json:"-"` + + Code *crypto.CryptoValue `json:"code,omitempty"` + Expiry time.Duration `json:"expiry,omitempty"` + NotificationType human.NotificationType `json:"notificationType,omitempty"` +} + +func (e *CodeAddedEvent) CheckPrevious() bool { + return false +} + +func (e *CodeAddedEvent) Data() interface{} { + return e +} + +func NewPasswordCodeAddedEvent( + ctx context.Context, + code *crypto.CryptoValue, + expiry time.Duration, + notificationType human.NotificationType, +) *CodeAddedEvent { + return &CodeAddedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + HumanPasswordCodeAddedType, + ), + Code: code, + Expiry: expiry, + NotificationType: notificationType, + } +} + +func CodeAddedEventMapper(event *repository.Event) (eventstore.EventReader, error) { + humanAdded := &CodeAddedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + } + err := json.Unmarshal(event.Data, humanAdded) + if err != nil { + return nil, errors.ThrowInternal(err, "USER-Ms90d", "unable to unmarshal human password code added") + } + + return humanAdded, nil +} + +type CodeSentEvent struct { + eventstore.BaseEvent `json:"-"` +} + +func (e *CodeSentEvent) CheckPrevious() bool { + return false +} + +func (e *CodeSentEvent) Data() interface{} { + return nil +} + +func NewCodeSentEvent(ctx context.Context) *CodeSentEvent { + return &CodeSentEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + HumanPasswordCodeSentType, + ), + } +} + +func CodeSentEventMapper(event *repository.Event) (eventstore.EventReader, error) { + return &CodeSentEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + }, nil +} + +type CheckSucceededEvent struct { + eventstore.BaseEvent `json:"-"` +} + +func (e *CheckSucceededEvent) CheckPrevious() bool { + return false +} + +func (e *CheckSucceededEvent) Data() interface{} { + return nil +} + +func NewCheckSucceededEvent(ctx context.Context) *CheckSucceededEvent { + return &CheckSucceededEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + HumanPasswordCheckSucceededType, + ), + } +} + +func CheckSucceededEventMapper(event *repository.Event) (eventstore.EventReader, error) { + return &CheckSucceededEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + }, nil +} + +type CheckFailedEvent struct { + eventstore.BaseEvent `json:"-"` +} + +func (e *CheckFailedEvent) CheckPrevious() bool { + return false +} + +func (e *CheckFailedEvent) Data() interface{} { + return nil +} + +func NewCheckFailedEvent(ctx context.Context) *CheckFailedEvent { + return &CheckFailedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + HumanPasswordCheckFailedType, + ), + } +} + +func CheckFailedEventMapper(event *repository.Event) (eventstore.EventReader, error) { + return &CheckFailedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + }, nil +} diff --git a/internal/v2/repository/user/human/phone/events.go b/internal/v2/repository/user/human/phone/events.go new file mode 100644 index 0000000000..11422ea9c4 --- /dev/null +++ b/internal/v2/repository/user/human/phone/events.go @@ -0,0 +1,210 @@ +package phone + +import ( + "context" + "encoding/json" + "github.com/caos/zitadel/internal/crypto" + "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/v2" + "github.com/caos/zitadel/internal/eventstore/v2/repository" + "time" +) + +const ( + phoneEventPrefix = eventstore.EventType("user.human.phone.") + HumanPhoneChangedType = phoneEventPrefix + "changed" + HumanPhoneRemovedType = phoneEventPrefix + "removed" + HumanPhoneVerifiedType = phoneEventPrefix + "verified" + HumanPhoneVerificationFailedType = phoneEventPrefix + "verification.failed" + HumanPhoneCodeAddedType = phoneEventPrefix + "code.added" + HumanPhoneCodeSentType = phoneEventPrefix + "code.sent" +) + +type ChangedEvent struct { + eventstore.BaseEvent `json:"-"` + + PhoneNumber string `json:"phone,omitempty"` +} + +func (e *ChangedEvent) CheckPrevious() bool { + return true +} + +func (e *ChangedEvent) Data() interface{} { + return e +} + +func NewChangedEvent(ctx context.Context, phone string) *ChangedEvent { + return &ChangedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + HumanPhoneChangedType, + ), + PhoneNumber: phone, + } +} + +func ChangedEventMapper(event *repository.Event) (eventstore.EventReader, error) { + phoneChangedEvent := &ChangedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + } + err := json.Unmarshal(event.Data, phoneChangedEvent) + if err != nil { + return nil, errors.ThrowInternal(err, "USER-5M0pd", "unable to unmarshal human phone changed") + } + + return phoneChangedEvent, nil +} + +type RemovedEvent struct { + eventstore.BaseEvent `json:"-"` +} + +func (e *RemovedEvent) CheckPrevious() bool { + return true +} + +func (e *RemovedEvent) Data() interface{} { + return nil +} + +func NewRemovedEvent(ctx context.Context) *RemovedEvent { + return &RemovedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + HumanPhoneRemovedType, + ), + } +} + +func RemovedEventMapper(event *repository.Event) (eventstore.EventReader, error) { + return &ChangedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + }, nil +} + +type VerifiedEvent struct { + eventstore.BaseEvent `json:"-"` + + IsPhoneVerified bool `json:"-"` +} + +func (e *VerifiedEvent) CheckPrevious() bool { + return true +} + +func (e *VerifiedEvent) Data() interface{} { + return nil +} + +func NewVerifiedEvent(ctx context.Context) *VerifiedEvent { + return &VerifiedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + HumanPhoneVerifiedType, + ), + } +} + +func VerifiedEventMapper(event *repository.Event) (eventstore.EventReader, error) { + return &VerifiedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + IsPhoneVerified: true, + }, nil +} + +type VerificationFailedEvent struct { + eventstore.BaseEvent `json:"-"` +} + +func (e *VerificationFailedEvent) CheckPrevious() bool { + return true +} + +func (e *VerificationFailedEvent) Data() interface{} { + return nil +} + +func NewVerificationFailedEvent(ctx context.Context) *VerificationFailedEvent { + return &VerificationFailedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + HumanPhoneVerificationFailedType, + ), + } +} + +func VerificationFailedEventMapper(event *repository.Event) (eventstore.EventReader, error) { + return &VerificationFailedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + }, nil +} + +type CodeAddedEvent struct { + eventstore.BaseEvent `json:"-"` + + Code *crypto.CryptoValue `json:"code,omitempty"` + Expiry time.Duration `json:"expiry,omitempty"` +} + +func (e *CodeAddedEvent) CheckPrevious() bool { + return true +} + +func (e *CodeAddedEvent) Data() interface{} { + return e +} + +func NewCodeAddedEvent( + ctx context.Context, + code *crypto.CryptoValue, + expiry time.Duration, +) *CodeAddedEvent { + return &CodeAddedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + HumanPhoneCodeAddedType, + ), + Code: code, + Expiry: expiry, + } +} + +func CodeAddedEventMapper(event *repository.Event) (eventstore.EventReader, error) { + codeAdded := &CodeAddedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + } + err := json.Unmarshal(event.Data, codeAdded) + if err != nil { + return nil, errors.ThrowInternal(err, "USER-6Ms9d", "unable to unmarshal human phone code added") + } + + return codeAdded, nil +} + +type CodeSentEvent struct { + eventstore.BaseEvent `json:"-"` +} + +func (e *CodeSentEvent) CheckPrevious() bool { + return false +} + +func (e *CodeSentEvent) Data() interface{} { + return e +} + +func NewCodeSentEvent(ctx context.Context) *CodeSentEvent { + return &CodeSentEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + HumanPhoneCodeSentType, + ), + } +} + +func CodeSentEventMapper(event *repository.Event) (eventstore.EventReader, error) { + return &CodeSentEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + }, nil +} diff --git a/internal/v2/repository/user/human/profile/events.go b/internal/v2/repository/user/human/profile/events.go new file mode 100644 index 0000000000..b67483281d --- /dev/null +++ b/internal/v2/repository/user/human/profile/events.go @@ -0,0 +1,84 @@ +package profile + +import ( + "context" + "encoding/json" + "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/v2" + "github.com/caos/zitadel/internal/eventstore/v2/repository" + "github.com/caos/zitadel/internal/v2/repository/user/human" + "golang.org/x/text/language" +) + +const ( + profileEventPrefix = eventstore.EventType("user.human.profile.") + HumanProfileChangedType = profileEventPrefix + "changed" +) + +type ChangedEvent struct { + eventstore.BaseEvent `json:"-"` + + FirstName string `json:"firstName,omitempty"` + LastName string `json:"lastName,omitempty"` + NickName string `json:"nickName,omitempty"` + DisplayName string `json:"displayName,omitempty"` + PreferredLanguage language.Tag `json:"preferredLanguage,omitempty"` + Gender human.Gender `json:"gender,omitempty"` +} + +func (e *ChangedEvent) CheckPrevious() bool { + return true +} + +func (e *ChangedEvent) Data() interface{} { + return e +} + +func NewChangedEvent( + ctx context.Context, + current *WriteModel, + firstName, + lastName, + nickName, + displayName string, + preferredLanguage language.Tag, + gender human.Gender, +) *ChangedEvent { + e := &ChangedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + HumanProfileChangedType, + ), + } + if current.FirstName != firstName { + e.FirstName = firstName + } + if current.LastName != lastName { + e.LastName = lastName + } + if current.NickName != nickName { + e.NickName = nickName + } + if current.DisplayName != displayName { + e.DisplayName = displayName + } + if current.PreferredLanguage != preferredLanguage { + e.PreferredLanguage = preferredLanguage + } + if current.Gender != gender { + e.Gender = gender + } + return e +} + +func ChangedEventMapper(event *repository.Event) (eventstore.EventReader, error) { + profileChanged := &ChangedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + } + err := json.Unmarshal(event.Data, profileChanged) + if err != nil { + return nil, errors.ThrowInternal(err, "USER-5M0pd", "unable to unmarshal human profile changed") + } + + return profileChanged, nil +} diff --git a/internal/v2/repository/user/human/profile/write_model.go b/internal/v2/repository/user/human/profile/write_model.go new file mode 100644 index 0000000000..767e61046e --- /dev/null +++ b/internal/v2/repository/user/human/profile/write_model.go @@ -0,0 +1,18 @@ +package profile + +import ( + "github.com/caos/zitadel/internal/eventstore/v2" + "github.com/caos/zitadel/internal/v2/repository/user/human" + "golang.org/x/text/language" +) + +type WriteModel struct { + eventstore.WriteModel + + FirstName string + LastName string + NickName string + DisplayName string + PreferredLanguage language.Tag + Gender human.Gender +} diff --git a/internal/v2/repository/user/machine/events.go b/internal/v2/repository/user/machine/events.go new file mode 100644 index 0000000000..1dd70b15a9 --- /dev/null +++ b/internal/v2/repository/user/machine/events.go @@ -0,0 +1,107 @@ +package machine + +import ( + "context" + "encoding/json" + "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/v2" + "github.com/caos/zitadel/internal/eventstore/v2/repository" +) + +const ( + machineEventPrefix = eventstore.EventType("user.machine.") + MachineAddedEventType = machineEventPrefix + "added" + MachineChangedEventType = machineEventPrefix + "changed" +) + +type AddedEvent struct { + eventstore.BaseEvent `json:"-"` + + UserName string `json:"userName"` + + Name string `json:"name,omitempty"` + Description string `json:"description,omitempty"` +} + +func (e *AddedEvent) CheckPrevious() bool { + return false +} + +func (e *AddedEvent) Data() interface{} { + return e +} + +func NewAddedEvent( + ctx context.Context, + userName, + name, + description string, +) *AddedEvent { + return &AddedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + MachineAddedEventType, + ), + UserName: userName, + Name: name, + Description: description, + } +} + +func AddedEventMapper(event *repository.Event) (eventstore.EventReader, error) { + machineAdded := &AddedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + } + err := json.Unmarshal(event.Data, machineAdded) + if err != nil { + return nil, errors.ThrowInternal(err, "USER-tMv9s", "unable to unmarshal machine added") + } + + return machineAdded, nil +} + +type ChangedEvent struct { + eventstore.BaseEvent `json:"-"` + + UserName string `json:"userName"` + + Name string `json:"name,omitempty"` + Description string `json:"description,omitempty"` +} + +func (e *ChangedEvent) CheckPrevious() bool { + return false +} + +func (e *ChangedEvent) Data() interface{} { + return e +} + +func NewChangedEvent( + ctx context.Context, + userName, + name, + description string, +) *ChangedEvent { + return &ChangedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + MachineChangedEventType, + ), + UserName: userName, + Name: name, + Description: description, + } +} + +func ChangedEventMapper(event *repository.Event) (eventstore.EventReader, error) { + machineChanged := &ChangedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + } + err := json.Unmarshal(event.Data, machineChanged) + if err != nil { + return nil, errors.ThrowInternal(err, "USER-4M9ds", "unable to unmarshal machine changed") + } + + return machineChanged, nil +} diff --git a/internal/v2/repository/user/machine/keys/events.go b/internal/v2/repository/user/machine/keys/events.go new file mode 100644 index 0000000000..dff13e0442 --- /dev/null +++ b/internal/v2/repository/user/machine/keys/events.go @@ -0,0 +1,103 @@ +package keys + +import ( + "context" + "encoding/json" + "github.com/caos/zitadel/internal/errors" + "github.com/caos/zitadel/internal/eventstore/v2" + "github.com/caos/zitadel/internal/eventstore/v2/repository" + "time" +) + +const ( + machineKeyEventPrefix = eventstore.EventType("user.machine.key.") + MachineKeyAddedEventType = machineKeyEventPrefix + "added" + MachineKeyRemovedEventType = machineKeyEventPrefix + "removed" +) + +type AddedEvent struct { + eventstore.BaseEvent `json:"-"` + + KeyID string `json:"keyId,omitempty"` + Type MachineKeyType `json:"type,omitempty"` + ExpirationDate time.Time `json:"expirationDate,omitempty"` + PublicKey []byte `json:"publicKey,omitempty"` +} + +func (e *AddedEvent) CheckPrevious() bool { + return false +} + +func (e *AddedEvent) Data() interface{} { + return e +} + +func NewAddedEvent( + ctx context.Context, + keyID string, + keyType MachineKeyType, + expirationDate time.Time, + publicKey []byte, +) *AddedEvent { + return &AddedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + MachineKeyAddedEventType, + ), + KeyID: keyID, + Type: keyType, + ExpirationDate: expirationDate, + PublicKey: publicKey, + } +} + +func AddedEventMapper(event *repository.Event) (eventstore.EventReader, error) { + machineAdded := &AddedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + } + err := json.Unmarshal(event.Data, machineAdded) + if err != nil { + return nil, errors.ThrowInternal(err, "USER-rEs8f", "unable to unmarshal machine key added") + } + + return machineAdded, nil +} + +type RemovedEvent struct { + eventstore.BaseEvent `json:"-"` + + KeyID string `json:"keyId,omitempty"` +} + +func (e *RemovedEvent) CheckPrevious() bool { + return false +} + +func (e *RemovedEvent) Data() interface{} { + return e +} + +func NewRemovedEvent( + ctx context.Context, + keyID string, +) *RemovedEvent { + return &RemovedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + MachineKeyRemovedEventType, + ), + KeyID: keyID, + } +} + +func RemovedEventMapper(event *repository.Event) (eventstore.EventReader, error) { + machineRemoved := &RemovedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(event), + } + err := json.Unmarshal(event.Data, machineRemoved) + if err != nil { + return nil, errors.ThrowInternal(err, "USER-5Gm9s", "unable to unmarshal machine key removed") + } + + return machineRemoved, nil +} diff --git a/internal/v2/repository/user/machine/keys/key.go b/internal/v2/repository/user/machine/keys/key.go new file mode 100644 index 0000000000..7c9b7ae438 --- /dev/null +++ b/internal/v2/repository/user/machine/keys/key.go @@ -0,0 +1,14 @@ +package keys + +type MachineKeyType int32 + +const ( + MachineKeyTypeNONE = iota + MachineKeyTypeJSON + + keyCount +) + +func (f MachineKeyType) Valid() bool { + return f >= 0 && f < keyCount +} diff --git a/internal/v2/repository/user/read_model.go b/internal/v2/repository/user/read_model.go new file mode 100644 index 0000000000..5caf9f51f3 --- /dev/null +++ b/internal/v2/repository/user/read_model.go @@ -0,0 +1,52 @@ +package user + +import ( + "github.com/caos/zitadel/internal/eventstore/v2" +) + +type ReadModel struct { + eventstore.ReadModel +} + +func NewReadModel(id string) *ReadModel { + return &ReadModel{ + ReadModel: eventstore.ReadModel{ + AggregateID: id, + }, + } +} + +func (rm *ReadModel) AppendEvents(events ...eventstore.EventReader) { + rm.ReadModel.AppendEvents(events...) + for _, event := range events { + switch event.(type) { + // TODO: implement append events + } + } +} + +func (rm *ReadModel) Reduce() (err error) { + for _, event := range rm.Events { + switch event.(type) { + //TODO: implement reduce + } + } + for _, reduce := range []func() error{ + rm.ReadModel.Reduce, + } { + if err = reduce(); err != nil { + return err + } + } + + return nil +} + +func (rm *ReadModel) AppendAndReduce(events ...eventstore.EventReader) error { + rm.AppendEvents(events...) + return rm.Reduce() +} + +func (rm *ReadModel) Query() *eventstore.SearchQueryBuilder { + return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent, AggregateType).AggregateIDs(rm.AggregateID) +} diff --git a/internal/v2/repository/user/v1/events.go b/internal/v2/repository/user/v1/events.go new file mode 100644 index 0000000000..299fa657ca --- /dev/null +++ b/internal/v2/repository/user/v1/events.go @@ -0,0 +1,469 @@ +package v1 + +import ( + "context" + "github.com/caos/zitadel/internal/crypto" + "github.com/caos/zitadel/internal/eventstore/v2" + "github.com/caos/zitadel/internal/v2/repository/user/human" + "github.com/caos/zitadel/internal/v2/repository/user/human/address" + "github.com/caos/zitadel/internal/v2/repository/user/human/email" + "github.com/caos/zitadel/internal/v2/repository/user/human/mfa" + "github.com/caos/zitadel/internal/v2/repository/user/human/mfa/otp" + "github.com/caos/zitadel/internal/v2/repository/user/human/password" + "github.com/caos/zitadel/internal/v2/repository/user/human/phone" + "github.com/caos/zitadel/internal/v2/repository/user/human/profile" + "golang.org/x/text/language" + "time" +) + +const ( + userEventTypePrefix = eventstore.EventType("user.") + UserV1AddedType = userEventTypePrefix + "added" + UserV1RegisteredType = userEventTypePrefix + "selfregistered" + UserV1InitialCodeAddedType = userEventTypePrefix + "initialization.code.added" + UserV1InitialCodeSentType = userEventTypePrefix + "initialization.code.sent" + UserV1InitializedCheckSucceededType = userEventTypePrefix + "initialization.check.succeeded" + UserV1InitializedCheckFailedType = userEventTypePrefix + "initialization.check.failed" + UserV1SignedOutType = userEventTypePrefix + "signed.out" + + userV1PasswordEventTypePrefix = userEventTypePrefix + "password." + UserV1PasswordChangedType = userV1PasswordEventTypePrefix + "changed" + UserV1PasswordCodeAddedType = userV1PasswordEventTypePrefix + "code.added" + UserV1PasswordCodeSentType = userV1PasswordEventTypePrefix + "code.sent" + UserV1PasswordCheckSucceededType = userV1PasswordEventTypePrefix + "check.succeeded" + UserV1PasswordCheckFailedType = userV1PasswordEventTypePrefix + "check.failed" + + userV1EmailEventTypePrefix = userEventTypePrefix + "email." + UserV1EmailChangedType = userV1EmailEventTypePrefix + "changed" + UserV1EmailVerifiedType = userV1EmailEventTypePrefix + "verified" + UserV1EmailVerificationFailedType = userV1EmailEventTypePrefix + "verification.failed" + UserV1EmailCodeAddedType = userV1EmailEventTypePrefix + "code.added" + UserV1EmailCodeSentType = userV1EmailEventTypePrefix + "code.sent" + + userV1PhoneEventTypePrefix = userEventTypePrefix + "phone." + UserV1PhoneChangedType = userV1PhoneEventTypePrefix + "changed" + UserV1PhoneRemovedType = userV1PhoneEventTypePrefix + "removed" + UserV1PhoneVerifiedType = userV1PhoneEventTypePrefix + "verified" + UserV1PhoneVerificationFailedType = userV1PhoneEventTypePrefix + "verification.failed" + UserV1PhoneCodeAddedType = userV1PhoneEventTypePrefix + "code.added" + UserV1PhoneCodeSentType = userV1PhoneEventTypePrefix + "code.sent" + + userV1ProfileEventTypePrefix = userEventTypePrefix + "profile." + UserV1ProfileChangedType = userV1ProfileEventTypePrefix + "changed" + + userV1AddressEventTypePrefix = userEventTypePrefix + "address." + UserV1AddressChangedType = userV1AddressEventTypePrefix + "changed" + + userV1MFAEventTypePrefix = userEventTypePrefix + "mfa." + UserV1MFAInitSkippedType = userV1MFAOTPEventTypePrefix + "init.skipped" + + userV1MFAOTPEventTypePrefix = userV1MFAEventTypePrefix + "otp." + UserV1MFAOTPAddedType = userV1MFAOTPEventTypePrefix + "added" + UserV1MFAOTPRemovedType = userV1MFAOTPEventTypePrefix + "removed" + UserV1MFAOTPVerifiedType = userV1MFAOTPEventTypePrefix + "verified" + UserV1MFAOTPCheckSucceededType = userV1MFAOTPEventTypePrefix + "check.succeeded" + UserV1MFAOTPCheckFailedType = userV1MFAOTPEventTypePrefix + "check.failed" +) + +func NewUserV1AddedEvent( + ctx context.Context, + userName, + firstName, + lastName, + nickName, + displayName string, + preferredLanguage language.Tag, + gender human.Gender, + emailAddress, + phoneNumber, + country, + locality, + postalCode, + region, + streetAddress string, +) *human.AddedEvent { + return &human.AddedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + UserV1AddedType, + ), + UserName: userName, + FirstName: firstName, + LastName: lastName, + NickName: nickName, + DisplayName: displayName, + PreferredLanguage: preferredLanguage, + Gender: gender, + EmailAddress: emailAddress, + PhoneNumber: phoneNumber, + Country: country, + Locality: locality, + PostalCode: postalCode, + Region: region, + StreetAddress: streetAddress, + } +} + +func NewUserV1RegisteredEvent( + ctx context.Context, + userName, + firstName, + lastName, + nickName, + displayName string, + preferredLanguage language.Tag, + gender int32, + emailAddress, + phoneNumber, + country, + locality, + postalCode, + region, + streetAddress string, +) *human.RegisteredEvent { + return &human.RegisteredEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + UserV1RegisteredType, + ), + UserName: userName, + FirstName: firstName, + LastName: lastName, + NickName: nickName, + DisplayName: displayName, + PreferredLanguage: preferredLanguage, + Gender: gender, + EmailAddress: emailAddress, + PhoneNumber: phoneNumber, + Country: country, + Locality: locality, + PostalCode: postalCode, + Region: region, + StreetAddress: streetAddress, + } +} + +func NewUserV1InitialCodeAddedEvent( + ctx context.Context, + code *crypto.CryptoValue, + expiry time.Duration, +) *human.InitialCodeAddedEvent { + return &human.InitialCodeAddedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + UserV1InitialCodeAddedType, + ), + Code: code, + Expiry: expiry, + } +} + +func NewUserV1InitialCodeSentEvent(ctx context.Context) *human.InitialCodeSentEvent { + return &human.InitialCodeSentEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + UserV1InitialCodeSentType, + ), + } +} + +func NewUserV1InitializedCheckSucceededEvent(ctx context.Context) *human.InitializedCheckSucceededEvent { + return &human.InitializedCheckSucceededEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + UserV1InitializedCheckSucceededType, + ), + } +} + +func NewUserV1InitializedCheckFailedEvent(ctx context.Context) *human.InitializedCheckFailedEvent { + return &human.InitializedCheckFailedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + UserV1InitializedCheckFailedType, + ), + } +} + +func NewUserV1SignedOutEvent(ctx context.Context) *human.SignedOutEvent { + return &human.SignedOutEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + UserV1SignedOutType, + ), + } +} + +func NewUserV1PasswordChangedEvent( + ctx context.Context, + secret *crypto.CryptoValue, + changeRequired bool, +) *password.ChangedEvent { + return &password.ChangedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + UserV1PasswordChangedType, + ), + Secret: secret, + ChangeRequired: changeRequired, + } +} + +func NewUserV1PasswordCodeAddedEvent( + ctx context.Context, + code *crypto.CryptoValue, + expiry time.Duration, + notificationType human.NotificationType, +) *password.CodeAddedEvent { + return &password.CodeAddedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + UserV1PasswordCodeAddedType, + ), + Code: code, + Expiry: expiry, + NotificationType: notificationType, + } +} + +func NewUserV1PasswordCodeSentEvent(ctx context.Context) *password.CodeSentEvent { + return &password.CodeSentEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + UserV1PasswordCodeSentType, + ), + } +} + +func NewUserV1PasswordCheckSucceededEvent(ctx context.Context) *password.CheckSucceededEvent { + return &password.CheckSucceededEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + UserV1PasswordCheckSucceededType, + ), + } +} + +func NewUserV1PasswordCheckFailedEvent(ctx context.Context) *password.CheckFailedEvent { + return &password.CheckFailedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + UserV1PasswordCheckFailedType, + ), + } +} + +func NewUserV1EmailChangedEvent(ctx context.Context, emailAddress string) *email.ChangedEvent { + return &email.ChangedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + UserV1EmailChangedType, + ), + EmailAddress: emailAddress, + } +} + +func NewUserV1EmailVerifiedEvent(ctx context.Context) *email.VerifiedEvent { + return &email.VerifiedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + UserV1EmailVerifiedType, + ), + } +} + +func NewUserV1EmailVerificationFailedEvent(ctx context.Context) *email.VerificationFailedEvent { + return &email.VerificationFailedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + UserV1EmailVerificationFailedType, + ), + } +} + +func NewUserV1EmailCodeAddedEvent( + ctx context.Context, + code *crypto.CryptoValue, + expiry time.Duration, +) *email.CodeAddedEvent { + return &email.CodeAddedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + UserV1EmailCodeAddedType, + ), + Code: code, + Expiry: expiry, + } +} + +func NewUserV1EmailCodeSentEvent(ctx context.Context) *email.CodeSentEvent { + return &email.CodeSentEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + UserV1EmailCodeSentType, + ), + } +} + +func NewUserV1PhoneChangedEvent(ctx context.Context, phone string) *phone.ChangedEvent { + return phone.HumanPhoneChangedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + UserV1PhoneChangedType, + ), + PhoneNumber: phone, + } +} + +func NewUserV1PhoneRemovedEvent(ctx context.Context) *phone.RemovedEvent { + return &phone.RemovedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + UserV1PhoneRemovedType, + ), + } +} + +func NewUserV1PhoneVerifiedEvent(ctx context.Context) *phone.VerifiedEvent { + return &phone.VerifiedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + UserV1PhoneVerifiedType, + ), + } +} + +func NewUserV1PhoneVerificationFailedEvent(ctx context.Context) *phone.VerificationFailedEvent { + return &phone.VerificationFailedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + UserV1PhoneVerificationFailedType, + ), + } +} + +func NewUserV1PhoneCodeAddedEvent( + ctx context.Context, + code *crypto.CryptoValue, + expiry time.Duration, +) *phone.CodeAddedEvent { + return &phone.CodeAddedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + UserV1PhoneCodeAddedType, + ), + Code: code, + Expiry: expiry, + } +} + +func NewUserV1PhoneCodeSentEvent(ctx context.Context) *phone.CodeSentEvent { + return &phone.CodeSentEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + UserV1PhoneCodeSentType, + ), + } +} + +func NewUserV1ProfileChangedEvent( + ctx context.Context, + firstName, + lastName, + nickName, + displayName string, + preferredLanguage language.Tag, + gender human.Gender, +) *profile.ChangedEvent { + return &profile.ChangedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + UserV1ProfileChangedType, + ), + FirstName: firstName, + LastName: lastName, + NickName: nickName, + DisplayName: displayName, + PreferredLanguage: preferredLanguage, + Gender: gender, + } +} + +func NewUserV1AddressChangedEvent( + ctx context.Context, + country, + locality, + postalCode, + region, + streetAddress string, +) *address.HumanAddressChangedEvent { + return &address.HumanAddressChangedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + UserV1AddressChangedType, + ), + Country: country, + Locality: locality, + PostalCode: postalCode, + Region: region, + StreetAddress: streetAddress, + } +} + +func NewUserV1MFAInitSkippedEvent(ctx context.Context) *mfa.InitSkippedEvent { + return &mfa.InitSkippedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + UserV1MFAInitSkippedType, + ), + } +} + +func NewUserV1MFAOTPAddedEvent( + ctx context.Context, + secret *crypto.CryptoValue, +) *otp.AddedEvent { + return &otp.AddedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + UserV1MFAOTPAddedType, + ), + Secret: secret, + } +} + +func NewUserV1MFAOTPVerifiedEvent(ctx context.Context) *otp.VerifiedEvent { + return &otp.VerifiedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + UserV1MFAOTPVerifiedType, + ), + } +} + +func NewUserV1MFAOTPRemovedEvent(ctx context.Context) *otp.RemovedEvent { + return &otp.RemovedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + UserV1MFAOTPRemovedType, + ), + } +} + +func NewUserV1MFAOTPCheckSucceededEvent(ctx context.Context) *otp.CheckSucceededEvent { + return &otp.CheckSucceededEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + UserV1MFAOTPCheckSucceededType, + ), + } +} + +func NewUserV1MFAOTPCheckFailedEvent(ctx context.Context) *otp.CheckFailedEvent { + return &otp.CheckFailedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + UserV1MFAOTPCheckFailedType, + ), + } +}