mirror of
https://github.com/zitadel/zitadel.git
synced 2024-12-04 23:45:07 +00:00
split user projection
This commit is contained in:
parent
81aef2cf5e
commit
a4ed2373ab
@ -3,8 +3,6 @@ package admin
|
||||
import (
|
||||
"context"
|
||||
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/authz"
|
||||
"github.com/zitadel/zitadel/internal/api/grpc/object"
|
||||
org_grpc "github.com/zitadel/zitadel/internal/api/grpc/org"
|
||||
@ -12,12 +10,7 @@ import (
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/query"
|
||||
// cmd_v2 "github.com/zitadel/zitadel/internal/v2/command"
|
||||
"github.com/zitadel/zitadel/internal/v2/org"
|
||||
"github.com/zitadel/zitadel/internal/v2/projection"
|
||||
"github.com/zitadel/zitadel/internal/v2/readmodel"
|
||||
admin_pb "github.com/zitadel/zitadel/pkg/grpc/admin"
|
||||
object_pb "github.com/zitadel/zitadel/pkg/grpc/object"
|
||||
org_pb "github.com/zitadel/zitadel/pkg/grpc/org"
|
||||
)
|
||||
|
||||
func (s *Server) IsOrgUnique(ctx context.Context, req *admin_pb.IsOrgUniqueRequest) (*admin_pb.IsOrgUniqueResponse, error) {
|
||||
@ -67,46 +60,6 @@ func (s *Server) GetOrgByID(ctx context.Context, req *admin_pb.GetOrgByIDRequest
|
||||
return &admin_pb.GetOrgByIDResponse{Org: org_grpc.OrgViewToPb(org)}, nil
|
||||
}
|
||||
|
||||
func orgToPb(org *readmodel.Org) *org_pb.Org {
|
||||
res := &org_pb.Org{
|
||||
Id: org.ID,
|
||||
State: stateToPb(org.State),
|
||||
Name: org.Name,
|
||||
PrimaryDomain: org.PrimaryDomain.Domain,
|
||||
Details: &object_pb.ObjectDetails{
|
||||
Sequence: uint64(org.Sequence),
|
||||
CreationDate: timestamppb.New(org.CreationDate),
|
||||
ChangeDate: timestamppb.New(org.ChangeDate),
|
||||
ResourceOwner: org.Owner,
|
||||
},
|
||||
}
|
||||
|
||||
if !org.CreationDate.IsZero() {
|
||||
res.Details.CreationDate = timestamppb.New(org.CreationDate)
|
||||
}
|
||||
|
||||
if !org.ChangeDate.IsZero() {
|
||||
res.Details.ChangeDate = timestamppb.New(org.ChangeDate)
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
|
||||
func stateToPb(state *projection.OrgState) org_pb.OrgState {
|
||||
switch state.State {
|
||||
case org.ActiveState:
|
||||
return org_pb.OrgState_ORG_STATE_ACTIVE
|
||||
case org.InactiveState:
|
||||
return org_pb.OrgState_ORG_STATE_INACTIVE
|
||||
case org.RemovedState:
|
||||
return org_pb.OrgState_ORG_STATE_REMOVED
|
||||
case org.UndefinedState:
|
||||
fallthrough
|
||||
default:
|
||||
return org_pb.OrgState_ORG_STATE_UNSPECIFIED
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) ListOrgs(ctx context.Context, req *admin_pb.ListOrgsRequest) (*admin_pb.ListOrgsResponse, error) {
|
||||
queries, err := listOrgRequestToModel(req)
|
||||
if err != nil {
|
||||
|
@ -1,8 +1,6 @@
|
||||
package projection
|
||||
|
||||
import (
|
||||
"golang.org/x/text/language"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/v2/eventstore"
|
||||
"github.com/zitadel/zitadel/internal/v2/user"
|
||||
@ -124,351 +122,3 @@ func (u *User) Filter() []*eventstore.Filter {
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
type Machine struct {
|
||||
projection
|
||||
|
||||
id string
|
||||
Name string
|
||||
Description string
|
||||
AccessTokenType domain.OIDCTokenType
|
||||
|
||||
// TODO: separate projection?
|
||||
Secret *string
|
||||
}
|
||||
|
||||
var _ eventstore.Reducer = (*Machine)(nil)
|
||||
|
||||
func NewMachineProjection(id string) *Machine {
|
||||
return &Machine{
|
||||
id: id,
|
||||
}
|
||||
}
|
||||
|
||||
// Reduce implements eventstore.Reducer.
|
||||
func (m *Machine) Reduce(events ...*eventstore.Event[eventstore.StoragePayload]) error {
|
||||
for _, event := range events {
|
||||
if !m.projection.shouldReduce(event) {
|
||||
continue
|
||||
}
|
||||
switch event.Type {
|
||||
case "user.machine.added":
|
||||
e, err := user.MachineAddedEventFromStorage(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m.Name = e.Payload.Name
|
||||
m.Description = e.Payload.Description
|
||||
m.AccessTokenType = e.Payload.AccessTokenType
|
||||
case "user.machine.changed":
|
||||
e, err := user.MachineChangedEventFromStorage(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if e.Payload.Name != nil {
|
||||
m.Name = *e.Payload.Name
|
||||
}
|
||||
if e.Payload.Description != nil {
|
||||
m.Description = *e.Payload.Description
|
||||
}
|
||||
if e.Payload.AccessTokenType != nil {
|
||||
m.AccessTokenType = *e.Payload.AccessTokenType
|
||||
}
|
||||
case "user.machine.secret.set":
|
||||
e, err := user.MachineSecretSetEventFromStorage(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m.Secret = &e.Payload.HashedSecret
|
||||
case "user.machine.secret.updated":
|
||||
e, err := user.MachineSecretHashUpdatedEventFromStorage(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m.Secret = &e.Payload.HashedSecret
|
||||
case "user.machine.secret.removed":
|
||||
e, err := user.MachineSecretHashUpdatedEventFromStorage(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m.Secret = &e.Payload.HashedSecret
|
||||
default:
|
||||
continue
|
||||
}
|
||||
m.projection.reduce(event)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Machine) Filter() []*eventstore.Filter {
|
||||
return []*eventstore.Filter{
|
||||
eventstore.NewFilter(
|
||||
eventstore.FilterPagination(
|
||||
eventstore.GlobalPositionGreater(&m.position),
|
||||
),
|
||||
eventstore.AppendAggregateFilter(
|
||||
user.AggregateType,
|
||||
eventstore.AggregateID(m.id),
|
||||
eventstore.AppendEvent(
|
||||
eventstore.EventTypes(
|
||||
"user.machine.added",
|
||||
"user.machine.changed",
|
||||
"user.machine.secret.set",
|
||||
"user.machine.secret.updated",
|
||||
"user.machine.secret.removed",
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
type Human struct {
|
||||
projection
|
||||
id string
|
||||
|
||||
FirstName string
|
||||
LastName string
|
||||
NickName string
|
||||
DisplayName string
|
||||
AvatarKey *string
|
||||
PreferredLanguage language.Tag
|
||||
Gender domain.Gender
|
||||
Email Email
|
||||
Phone *Phone
|
||||
PasswordChangeRequired bool
|
||||
}
|
||||
|
||||
type Phone struct {
|
||||
Number domain.PhoneNumber
|
||||
IsVerified bool
|
||||
}
|
||||
|
||||
type Email struct {
|
||||
Address domain.EmailAddress
|
||||
IsVerified bool
|
||||
}
|
||||
|
||||
var _ eventstore.Reducer = (*Human)(nil)
|
||||
|
||||
func NewHumanProjection(id string) *Human {
|
||||
return &Human{
|
||||
id: id,
|
||||
}
|
||||
}
|
||||
|
||||
// Reduce implements eventstore.Reducer.
|
||||
func (h *Human) Reduce(events ...*eventstore.Event[eventstore.StoragePayload]) error {
|
||||
for _, event := range events {
|
||||
if !h.projection.shouldReduce(event) {
|
||||
continue
|
||||
}
|
||||
|
||||
switch event.Type {
|
||||
case "user.human.added", "user.added":
|
||||
e, err := user.HumanAddedEventFromStorage(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
h.FirstName = e.Payload.FirstName
|
||||
h.LastName = e.Payload.LastName
|
||||
h.NickName = e.Payload.NickName
|
||||
h.DisplayName = e.Payload.DisplayName
|
||||
h.PreferredLanguage = e.Payload.PreferredLanguage
|
||||
h.Gender = e.Payload.Gender
|
||||
h.Email.Address = e.Payload.EmailAddress
|
||||
if e.Payload.PhoneNumber != "" {
|
||||
h.Phone = &Phone{
|
||||
Number: e.Payload.PhoneNumber,
|
||||
}
|
||||
}
|
||||
h.PasswordChangeRequired = e.Payload.PasswordChangeRequired
|
||||
case "user.human.selfregistered":
|
||||
e, err := user.HumanRegisteredEventFromStorage(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
h.FirstName = e.Payload.FirstName
|
||||
h.LastName = e.Payload.LastName
|
||||
h.NickName = e.Payload.NickName
|
||||
h.DisplayName = e.Payload.DisplayName
|
||||
h.PreferredLanguage = e.Payload.PreferredLanguage
|
||||
h.Gender = e.Payload.Gender
|
||||
h.Email.Address = e.Payload.EmailAddress
|
||||
if e.Payload.PhoneNumber != "" {
|
||||
h.Phone = &Phone{
|
||||
Number: e.Payload.PhoneNumber,
|
||||
}
|
||||
}
|
||||
h.PasswordChangeRequired = e.Payload.PasswordChangeRequired
|
||||
case "user.human.profile.changed":
|
||||
e, err := user.HumanProfileChangedEventFromStorage(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if e.Payload.FirstName != "" {
|
||||
h.FirstName = e.Payload.FirstName
|
||||
}
|
||||
if e.Payload.LastName != "" {
|
||||
h.LastName = e.Payload.LastName
|
||||
}
|
||||
if e.Payload.NickName != nil {
|
||||
h.NickName = *e.Payload.NickName
|
||||
}
|
||||
if e.Payload.DisplayName != nil {
|
||||
h.DisplayName = *e.Payload.DisplayName
|
||||
}
|
||||
if e.Payload.PreferredLanguage != nil {
|
||||
h.PreferredLanguage = *e.Payload.PreferredLanguage
|
||||
}
|
||||
if e.Payload.Gender != nil {
|
||||
h.Gender = *e.Payload.Gender
|
||||
}
|
||||
case "user.human.phone.changed":
|
||||
h.Phone = new(Phone)
|
||||
e, err := user.HumanPhoneChangedEventFromStorage(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
h.Phone.Number = e.Payload.PhoneNumber
|
||||
case "user.human.phone.removed":
|
||||
h.Phone = nil
|
||||
case "user.human.phone.verified":
|
||||
h.Phone.IsVerified = true
|
||||
case "user.human.email.changed":
|
||||
h.Email.IsVerified = false
|
||||
e, err := user.HumanEmailChangedEventFromStorage(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
h.Email.Address = e.Payload.Address
|
||||
case "user.human.email.verified":
|
||||
h.Email.IsVerified = true
|
||||
case "user.human.avatar.added":
|
||||
e, err := user.HumanAvatarAddedEventFromStorage(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
h.AvatarKey = &e.Payload.StoreKey
|
||||
case "user.human.avatar.removed":
|
||||
h.AvatarKey = nil
|
||||
case "user.human.password.changed":
|
||||
e, err := user.HumanPasswordChangedEventFromStorage(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
h.PasswordChangeRequired = e.Payload.ChangeRequired
|
||||
default:
|
||||
continue
|
||||
}
|
||||
h.projection.reduce(event)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *Human) Filter() []*eventstore.Filter {
|
||||
return []*eventstore.Filter{
|
||||
eventstore.NewFilter(
|
||||
eventstore.FilterPagination(
|
||||
eventstore.GlobalPositionGreater(&h.position),
|
||||
),
|
||||
eventstore.AppendAggregateFilter(
|
||||
user.AggregateType,
|
||||
eventstore.AggregateID(h.id),
|
||||
eventstore.AppendEvent(
|
||||
eventstore.EventTypes(
|
||||
"user.human.added",
|
||||
"user.added",
|
||||
"user.human.selfregistered",
|
||||
"user.human.profile.changed",
|
||||
"user.human.phone.changed",
|
||||
"user.human.phone.removed",
|
||||
"user.human.phone.verified",
|
||||
"user.human.email.changed",
|
||||
"user.human.email.verified",
|
||||
"user.human.avatar.added",
|
||||
"user.human.avatar.removed",
|
||||
"user.human.password.changed",
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
// import (
|
||||
// "github.com/zitadel/zitadel/internal/v2/eventstore"
|
||||
// "github.com/zitadel/zitadel/internal/v2/org"
|
||||
// )
|
||||
|
||||
// type OrgState struct {
|
||||
// projection
|
||||
|
||||
// id string
|
||||
|
||||
// org.State
|
||||
// }
|
||||
|
||||
// func NewStateProjection(id string) *OrgState {
|
||||
// // TODO: check buffer for id and return from buffer if exists
|
||||
// return &OrgState{
|
||||
// id: id,
|
||||
// }
|
||||
// }
|
||||
|
||||
// func (p *OrgState) Filter() []*eventstore.Filter {
|
||||
// return []*eventstore.Filter{
|
||||
// eventstore.NewFilter(
|
||||
// eventstore.FilterPagination(
|
||||
// eventstore.Descending(),
|
||||
// eventstore.GlobalPositionGreater(&p.position),
|
||||
// ),
|
||||
// eventstore.AppendAggregateFilter(
|
||||
// org.AggregateType,
|
||||
// eventstore.AggregateID(p.id),
|
||||
// eventstore.AppendEvent(
|
||||
// eventstore.EventType("org.added"),
|
||||
// ),
|
||||
// eventstore.AppendEvent(
|
||||
// eventstore.EventType("org.deactivated"),
|
||||
// ),
|
||||
// eventstore.AppendEvent(
|
||||
// eventstore.EventType("org.reactivated"),
|
||||
// ),
|
||||
// eventstore.AppendEvent(
|
||||
// eventstore.EventType("org.removed"),
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// }
|
||||
// }
|
||||
|
||||
// func (p *OrgState) Reduce(events ...*eventstore.Event[eventstore.StoragePayload]) error {
|
||||
// for _, event := range events {
|
||||
// if !p.shouldReduce(event) {
|
||||
// continue
|
||||
// }
|
||||
|
||||
// switch {
|
||||
// case org.Added.IsType(event.Type):
|
||||
// p.State = org.ActiveState
|
||||
// case org.Deactivated.IsType(event.Type):
|
||||
// p.State = org.InactiveState
|
||||
// case org.Reactivated.IsType(event.Type):
|
||||
// p.State = org.ActiveState
|
||||
// case org.Removed.IsType(event.Type):
|
||||
// p.State = org.RemovedState
|
||||
// default:
|
||||
// continue
|
||||
// }
|
||||
// p.position = event.Position
|
||||
// }
|
||||
|
||||
// // TODO: if more than x events store state
|
||||
|
||||
// return nil
|
||||
// }
|
||||
|
214
internal/v2/projection/user_human.go
Normal file
214
internal/v2/projection/user_human.go
Normal file
@ -0,0 +1,214 @@
|
||||
package projection
|
||||
|
||||
import (
|
||||
"golang.org/x/text/language"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/v2/eventstore"
|
||||
"github.com/zitadel/zitadel/internal/v2/user"
|
||||
)
|
||||
|
||||
type Human struct {
|
||||
projection
|
||||
id string
|
||||
|
||||
FirstName string
|
||||
LastName string
|
||||
NickName string
|
||||
DisplayName string
|
||||
AvatarKey *string
|
||||
PreferredLanguage language.Tag
|
||||
Gender domain.Gender
|
||||
Email Email
|
||||
Phone *Phone
|
||||
PasswordChangeRequired bool
|
||||
}
|
||||
|
||||
type Phone struct {
|
||||
Number domain.PhoneNumber
|
||||
IsVerified bool
|
||||
}
|
||||
|
||||
type Email struct {
|
||||
Address domain.EmailAddress
|
||||
IsVerified bool
|
||||
}
|
||||
|
||||
var _ eventstore.Reducer = (*Human)(nil)
|
||||
|
||||
func NewHumanProjection(id string) *Human {
|
||||
return &Human{
|
||||
id: id,
|
||||
}
|
||||
}
|
||||
|
||||
// Reduce implements eventstore.Reducer.
|
||||
func (h *Human) Reduce(events ...*eventstore.Event[eventstore.StoragePayload]) (err error) {
|
||||
for _, event := range events {
|
||||
if !h.projection.shouldReduce(event) {
|
||||
continue
|
||||
}
|
||||
|
||||
switch event.Type {
|
||||
case "user.human.added", "user.added":
|
||||
err = h.reduceAdded(event)
|
||||
case "user.human.selfregistered":
|
||||
err = h.reduceRegistered(event)
|
||||
case "user.human.profile.changed":
|
||||
err = h.reduceProfileChanged(event)
|
||||
case "user.human.phone.changed":
|
||||
err = h.reducePhoneChanged(event)
|
||||
case "user.human.phone.removed":
|
||||
h.Phone = nil
|
||||
case "user.human.phone.verified":
|
||||
h.Phone.IsVerified = true
|
||||
case "user.human.email.changed":
|
||||
err = h.reduceEmailChanged(event)
|
||||
case "user.human.email.verified":
|
||||
h.Email.IsVerified = true
|
||||
case "user.human.avatar.added":
|
||||
e, err := user.HumanAvatarAddedEventFromStorage(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
h.AvatarKey = &e.Payload.StoreKey
|
||||
case "user.human.avatar.removed":
|
||||
h.AvatarKey = nil
|
||||
case "user.human.password.changed":
|
||||
e, err := user.HumanPasswordChangedEventFromStorage(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
h.PasswordChangeRequired = e.Payload.ChangeRequired
|
||||
default:
|
||||
continue
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
h.projection.reduce(event)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *Human) Filter() []*eventstore.Filter {
|
||||
return []*eventstore.Filter{
|
||||
eventstore.NewFilter(
|
||||
eventstore.FilterPagination(
|
||||
eventstore.GlobalPositionGreater(&h.position),
|
||||
),
|
||||
eventstore.AppendAggregateFilter(
|
||||
user.AggregateType,
|
||||
eventstore.AggregateID(h.id),
|
||||
eventstore.AppendEvent(
|
||||
eventstore.EventTypes(
|
||||
"user.human.added",
|
||||
"user.added",
|
||||
"user.human.selfregistered",
|
||||
"user.human.profile.changed",
|
||||
"user.human.phone.changed",
|
||||
"user.human.phone.removed",
|
||||
"user.human.phone.verified",
|
||||
"user.human.email.changed",
|
||||
"user.human.email.verified",
|
||||
"user.human.avatar.added",
|
||||
"user.human.avatar.removed",
|
||||
"user.human.password.changed",
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
func (h *Human) reduceAdded(event *eventstore.Event[eventstore.StoragePayload]) error {
|
||||
e, err := user.HumanAddedEventFromStorage(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
h.FirstName = e.Payload.FirstName
|
||||
h.LastName = e.Payload.LastName
|
||||
h.NickName = e.Payload.NickName
|
||||
h.DisplayName = e.Payload.DisplayName
|
||||
h.PreferredLanguage = e.Payload.PreferredLanguage
|
||||
h.Gender = e.Payload.Gender
|
||||
h.Email.Address = e.Payload.EmailAddress
|
||||
if e.Payload.PhoneNumber != "" {
|
||||
h.Phone = &Phone{
|
||||
Number: e.Payload.PhoneNumber,
|
||||
}
|
||||
}
|
||||
h.PasswordChangeRequired = e.Payload.PasswordChangeRequired
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *Human) reduceRegistered(event *eventstore.Event[eventstore.StoragePayload]) error {
|
||||
e, err := user.HumanRegisteredEventFromStorage(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
h.FirstName = e.Payload.FirstName
|
||||
h.LastName = e.Payload.LastName
|
||||
h.NickName = e.Payload.NickName
|
||||
h.DisplayName = e.Payload.DisplayName
|
||||
h.PreferredLanguage = e.Payload.PreferredLanguage
|
||||
h.Gender = e.Payload.Gender
|
||||
h.Email.Address = e.Payload.EmailAddress
|
||||
if e.Payload.PhoneNumber != "" {
|
||||
h.Phone = &Phone{
|
||||
Number: e.Payload.PhoneNumber,
|
||||
}
|
||||
}
|
||||
h.PasswordChangeRequired = e.Payload.PasswordChangeRequired
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *Human) reduceProfileChanged(event *eventstore.Event[eventstore.StoragePayload]) error {
|
||||
e, err := user.HumanProfileChangedEventFromStorage(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if e.Payload.FirstName != "" {
|
||||
h.FirstName = e.Payload.FirstName
|
||||
}
|
||||
if e.Payload.LastName != "" {
|
||||
h.LastName = e.Payload.LastName
|
||||
}
|
||||
if e.Payload.NickName != nil {
|
||||
h.NickName = *e.Payload.NickName
|
||||
}
|
||||
if e.Payload.DisplayName != nil {
|
||||
h.DisplayName = *e.Payload.DisplayName
|
||||
}
|
||||
if e.Payload.PreferredLanguage != nil {
|
||||
h.PreferredLanguage = *e.Payload.PreferredLanguage
|
||||
}
|
||||
if e.Payload.Gender != nil {
|
||||
h.Gender = *e.Payload.Gender
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *Human) reducePhoneChanged(event *eventstore.Event[eventstore.StoragePayload]) error {
|
||||
h.Phone = new(Phone)
|
||||
e, err := user.HumanPhoneChangedEventFromStorage(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
h.Phone.Number = e.Payload.PhoneNumber
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *Human) reduceEmailChanged(event *eventstore.Event[eventstore.StoragePayload]) error {
|
||||
h.Email.IsVerified = false
|
||||
e, err := user.HumanEmailChangedEventFromStorage(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
h.Email.Address = e.Payload.Address
|
||||
return nil
|
||||
}
|
107
internal/v2/projection/user_machine.go
Normal file
107
internal/v2/projection/user_machine.go
Normal file
@ -0,0 +1,107 @@
|
||||
package projection
|
||||
|
||||
import (
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/v2/eventstore"
|
||||
"github.com/zitadel/zitadel/internal/v2/user"
|
||||
)
|
||||
|
||||
type Machine struct {
|
||||
projection
|
||||
|
||||
id string
|
||||
Name string
|
||||
Description string
|
||||
AccessTokenType domain.OIDCTokenType
|
||||
|
||||
// TODO: separate projection?
|
||||
Secret *string
|
||||
}
|
||||
|
||||
var _ eventstore.Reducer = (*Machine)(nil)
|
||||
|
||||
func NewMachineProjection(id string) *Machine {
|
||||
return &Machine{
|
||||
id: id,
|
||||
}
|
||||
}
|
||||
|
||||
// Reduce implements eventstore.Reducer.
|
||||
func (m *Machine) Reduce(events ...*eventstore.Event[eventstore.StoragePayload]) error {
|
||||
for _, event := range events {
|
||||
if !m.projection.shouldReduce(event) {
|
||||
continue
|
||||
}
|
||||
switch event.Type {
|
||||
case "user.machine.added":
|
||||
e, err := user.MachineAddedEventFromStorage(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
m.Name = e.Payload.Name
|
||||
m.Description = e.Payload.Description
|
||||
m.AccessTokenType = e.Payload.AccessTokenType
|
||||
case "user.machine.changed":
|
||||
e, err := user.MachineChangedEventFromStorage(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if e.Payload.Name != nil {
|
||||
m.Name = *e.Payload.Name
|
||||
}
|
||||
if e.Payload.Description != nil {
|
||||
m.Description = *e.Payload.Description
|
||||
}
|
||||
if e.Payload.AccessTokenType != nil {
|
||||
m.AccessTokenType = *e.Payload.AccessTokenType
|
||||
}
|
||||
case "user.machine.secret.set":
|
||||
e, err := user.MachineSecretSetEventFromStorage(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m.Secret = &e.Payload.HashedSecret
|
||||
case "user.machine.secret.updated":
|
||||
e, err := user.MachineSecretHashUpdatedEventFromStorage(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m.Secret = &e.Payload.HashedSecret
|
||||
case "user.machine.secret.removed":
|
||||
e, err := user.MachineSecretHashUpdatedEventFromStorage(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
m.Secret = &e.Payload.HashedSecret
|
||||
default:
|
||||
continue
|
||||
}
|
||||
m.projection.reduce(event)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Machine) Filter() []*eventstore.Filter {
|
||||
return []*eventstore.Filter{
|
||||
eventstore.NewFilter(
|
||||
eventstore.FilterPagination(
|
||||
eventstore.GlobalPositionGreater(&m.position),
|
||||
),
|
||||
eventstore.AppendAggregateFilter(
|
||||
user.AggregateType,
|
||||
eventstore.AggregateID(m.id),
|
||||
eventstore.AppendEvent(
|
||||
eventstore.EventTypes(
|
||||
"user.machine.added",
|
||||
"user.machine.changed",
|
||||
"user.machine.secret.set",
|
||||
"user.machine.secret.updated",
|
||||
"user.machine.secret.removed",
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
}
|
||||
}
|
206
internal/v2/projection/user_notify.go
Normal file
206
internal/v2/projection/user_notify.go
Normal file
@ -0,0 +1,206 @@
|
||||
package projection
|
||||
|
||||
import (
|
||||
"golang.org/x/text/language"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/v2/eventstore"
|
||||
"github.com/zitadel/zitadel/internal/v2/user"
|
||||
)
|
||||
|
||||
type NotifyUser struct {
|
||||
projection
|
||||
id string
|
||||
|
||||
Username string
|
||||
// LoginNames database.TextArray[string]
|
||||
// PreferredLoginName string
|
||||
FirstName string
|
||||
LastName string
|
||||
NickName string
|
||||
DisplayName string
|
||||
AvatarKey *string
|
||||
PreferredLanguage language.Tag
|
||||
Gender domain.Gender
|
||||
LastEmail *string
|
||||
VerifiedEmail *string
|
||||
LastPhone *string
|
||||
VerifiedPhone *string
|
||||
PasswordSet bool
|
||||
}
|
||||
|
||||
func NewNotifyUser(id string) *NotifyUser {
|
||||
return &NotifyUser{
|
||||
id: id,
|
||||
}
|
||||
}
|
||||
|
||||
func (n *NotifyUser) Reduce(events ...*eventstore.Event[eventstore.StoragePayload]) (err error) {
|
||||
for _, event := range events {
|
||||
if !n.shouldReduce(event) {
|
||||
continue
|
||||
}
|
||||
|
||||
switch event.Type {
|
||||
case "user.human.added", "user.added":
|
||||
err = n.reduceAdded(event)
|
||||
case "user.human.selfregistered":
|
||||
err = n.reduceRegistered(event)
|
||||
case "user.human.phone.changed":
|
||||
err = n.reducePhoneChanged(event)
|
||||
case "user.human.phone.removed":
|
||||
err = n.reducePhoneRemoved()
|
||||
case "user.human.phone.verified":
|
||||
err = n.reducePhoneVerified()
|
||||
case "user.human.email.changed":
|
||||
err = n.reduceEmailChanged(event)
|
||||
case "user.human.email.verified":
|
||||
err = n.reduceEmailVerified()
|
||||
case "user.human.avatar.added":
|
||||
err = n.reduceAvatarAdded(event)
|
||||
case "user.human.avatar.removed":
|
||||
err = n.reduceAvatarRemoved()
|
||||
case "user.human.password.changed":
|
||||
err = n.reducePasswordChanged()
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *NotifyUser) Filter() []*eventstore.Filter {
|
||||
return []*eventstore.Filter{
|
||||
eventstore.NewFilter(
|
||||
eventstore.FilterPagination(
|
||||
eventstore.GlobalPositionGreater(&n.position),
|
||||
),
|
||||
eventstore.AppendAggregateFilter(
|
||||
user.AggregateType,
|
||||
eventstore.AggregateID(n.id),
|
||||
eventstore.AppendEvent(
|
||||
eventstore.EventTypes(
|
||||
"user.human.added",
|
||||
"user.added",
|
||||
"user.human.selfregistered",
|
||||
"user.human.profile.changed",
|
||||
"user.human.phone.changed",
|
||||
"user.human.phone.removed",
|
||||
"user.human.phone.verified",
|
||||
"user.human.email.changed",
|
||||
"user.human.email.verified",
|
||||
"user.human.avatar.added",
|
||||
"user.human.avatar.removed",
|
||||
"user.human.password.changed",
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
func (n *NotifyUser) reduceAdded(event *eventstore.Event[eventstore.StoragePayload]) error {
|
||||
e, err := user.HumanAddedEventFromStorage(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
n.Username = e.Payload.Username
|
||||
n.FirstName = e.Payload.FirstName
|
||||
n.LastName = e.Payload.LastName
|
||||
n.NickName = e.Payload.NickName
|
||||
n.DisplayName = e.Payload.DisplayName
|
||||
n.PreferredLanguage = e.Payload.PreferredLanguage
|
||||
n.Gender = e.Payload.Gender
|
||||
if e.Payload.EmailAddress != "" {
|
||||
n.LastEmail = (*string)(&e.Payload.EmailAddress)
|
||||
}
|
||||
if e.Payload.PhoneNumber != "" {
|
||||
n.LastPhone = (*string)(&e.Payload.PhoneNumber)
|
||||
}
|
||||
n.PasswordSet = crypto.SecretOrEncodedHash(e.Payload.Secret, e.Payload.EncodedHash) != ""
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *NotifyUser) reduceRegistered(event *eventstore.Event[eventstore.StoragePayload]) error {
|
||||
e, err := user.HumanRegisteredEventFromStorage(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
n.Username = e.Payload.Username
|
||||
n.FirstName = e.Payload.FirstName
|
||||
n.LastName = e.Payload.LastName
|
||||
n.NickName = e.Payload.NickName
|
||||
n.DisplayName = e.Payload.DisplayName
|
||||
n.PreferredLanguage = e.Payload.PreferredLanguage
|
||||
n.Gender = e.Payload.Gender
|
||||
if e.Payload.EmailAddress != "" {
|
||||
n.LastEmail = (*string)(&e.Payload.EmailAddress)
|
||||
}
|
||||
if e.Payload.PhoneNumber != "" {
|
||||
n.LastPhone = (*string)(&e.Payload.PhoneNumber)
|
||||
}
|
||||
n.PasswordSet = crypto.SecretOrEncodedHash(e.Payload.Secret, e.Payload.EncodedHash) != ""
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *NotifyUser) reducePhoneChanged(event *eventstore.Event[eventstore.StoragePayload]) error {
|
||||
e, err := user.HumanPhoneChangedEventFromStorage(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
n.LastPhone = (*string)(&e.Payload.PhoneNumber)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *NotifyUser) reducePhoneRemoved() error {
|
||||
n.LastPhone = nil
|
||||
n.VerifiedPhone = nil
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *NotifyUser) reducePhoneVerified() error {
|
||||
n.VerifiedPhone = n.LastPhone
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *NotifyUser) reduceEmailChanged(event *eventstore.Event[eventstore.StoragePayload]) error {
|
||||
e, err := user.HumanEmailChangedEventFromStorage(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
n.LastEmail = (*string)(&e.Payload.Address)
|
||||
if e.Payload.Address == "" {
|
||||
n.LastEmail = nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *NotifyUser) reduceEmailVerified() error {
|
||||
n.VerifiedEmail = n.LastEmail
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *NotifyUser) reducePasswordChanged() error {
|
||||
n.PasswordSet = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *NotifyUser) reduceAvatarAdded(event *eventstore.Event[eventstore.StoragePayload]) error {
|
||||
e, err := user.HumanAvatarAddedEventFromStorage(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
n.AvatarKey = &e.Payload.StoreKey
|
||||
return nil
|
||||
}
|
||||
|
||||
func (n *NotifyUser) reduceAvatarRemoved() error {
|
||||
n.AvatarKey = nil
|
||||
return nil
|
||||
}
|
@ -16,6 +16,7 @@ type User struct {
|
||||
LoginNames *projection.LoginNames
|
||||
Human *projection.Human
|
||||
Machine *projection.Machine
|
||||
Notify *projection.NotifyUser
|
||||
}
|
||||
|
||||
func (u *User) PreferredLoginName() string {
|
||||
@ -36,12 +37,19 @@ func NewUser(id string) *User {
|
||||
User: *projection.NewUserProjection(id),
|
||||
Human: projection.NewHumanProjection(id),
|
||||
Machine: projection.NewMachineProjection(id),
|
||||
Notify: projection.NewNotifyUser(id),
|
||||
}
|
||||
}
|
||||
|
||||
func (u *User) Query(ctx context.Context, querier eventstore.Querier, opts ...QueryOpt) error {
|
||||
queryOpts := make([]eventstore.QueryOpt, 0, len(opts)+1)
|
||||
queryOpts = append(queryOpts, eventstore.AppendFilters(u.User.Filter()...), eventstore.AppendFilters(u.Human.Filter()...), eventstore.AppendFilters(u.Machine.Filter()...))
|
||||
queryOpts = append(queryOpts,
|
||||
eventstore.AppendFilters(u.User.Filter()...),
|
||||
eventstore.AppendFilters(u.Human.Filter()...),
|
||||
eventstore.AppendFilters(u.Machine.Filter()...),
|
||||
//TODO: adlerhurst we need to merge the queries because we double the events for humans at the moment
|
||||
// eventstore.AppendFilters(u.Notify.Filter()...),
|
||||
)
|
||||
for _, opt := range opts {
|
||||
queryOpts = opt(queryOpts)
|
||||
}
|
||||
@ -95,6 +103,7 @@ eventLoop:
|
||||
break eventLoop
|
||||
case "user.machine.added":
|
||||
u.Human = nil
|
||||
u.Notify = nil
|
||||
break eventLoop
|
||||
}
|
||||
}
|
||||
@ -103,7 +112,10 @@ eventLoop:
|
||||
}
|
||||
u.reduce(events[len(events)-1])
|
||||
if u.Type == domain.UserTypeHuman {
|
||||
err = u.Human.Reduce(events...)
|
||||
if err = u.Human.Reduce(events...); err != nil {
|
||||
return err
|
||||
}
|
||||
err = u.Notify.Reduce(events...)
|
||||
} else if u.Type == domain.UserTypeMachine {
|
||||
err = u.Machine.Reduce(events...)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user