From 1c40d5645e65b4f4dfdb5fc655da6f0f9d1c6770 Mon Sep 17 00:00:00 2001 From: Fabi <38692350+fgerschwiler@users.noreply.github.com> Date: Tue, 7 Jul 2020 19:31:51 +0200 Subject: [PATCH] feat: notification loginname (#381) * feat: add login names to notify user * feat: add login names to initial mail * feat: add login names to initial mail --- cmd/zitadel/startup.yaml | 1 + docs/local.md | 1 - .../eventsourcing/handler/handler.go | 7 +- .../eventsourcing/handler/notify_user.go | 115 ++++++++++++++- .../repository/eventsourcing/repository.go | 6 +- .../eventsourcing/spooler/spooler.go | 5 - .../eventsourcing/view/notify_user.go | 11 +- internal/notification/static/i18n/de.yaml | 2 +- internal/notification/static/i18n/en.yaml | 2 +- internal/notification/types/init_code.go | 7 +- internal/user/model/notify_user.go | 39 ++--- .../user/repository/view/model/notify_user.go | 133 +++++++++++------- .../view/model/notify_user_query.go | 61 ++++++++ internal/user/repository/view/notify_user.go | 17 ++- .../V1.3__notification_user_loginnames.sql | 6 + 15 files changed, 320 insertions(+), 93 deletions(-) create mode 100644 internal/user/repository/view/model/notify_user_query.go create mode 100644 migrations/cockroach/V1.3__notification_user_loginnames.sql diff --git a/cmd/zitadel/startup.yaml b/cmd/zitadel/startup.yaml index 3ff9499f69..c04e17938f 100644 --- a/cmd/zitadel/startup.yaml +++ b/cmd/zitadel/startup.yaml @@ -262,6 +262,7 @@ Console: Notification: Repository: DefaultLanguage: 'de' + Domain: $ZITADEL_DEFAULT_DOMAIN Eventstore: ServiceName: 'Notification' Repository: diff --git a/docs/local.md b/docs/local.md index 5199498666..968aff6373 100644 --- a/docs/local.md +++ b/docs/local.md @@ -31,4 +31,3 @@ cockroachdb/cockroach:v19.2.2 start --insecure #### Should show eventstore, management, admin, auth `show databases;` - diff --git a/internal/notification/repository/eventsourcing/handler/handler.go b/internal/notification/repository/eventsourcing/handler/handler.go index cfa6cf8bc6..18c6218bef 100644 --- a/internal/notification/repository/eventsourcing/handler/handler.go +++ b/internal/notification/repository/eventsourcing/handler/handler.go @@ -9,6 +9,7 @@ import ( "github.com/caos/zitadel/internal/eventstore/spooler" "github.com/caos/zitadel/internal/i18n" "github.com/caos/zitadel/internal/notification/repository/eventsourcing/view" + org_event "github.com/caos/zitadel/internal/org/repository/eventsourcing" usr_event "github.com/caos/zitadel/internal/user/repository/eventsourcing" "net/http" "time" @@ -29,6 +30,7 @@ type handler struct { type EventstoreRepos struct { UserEvents *usr_event.UserEventstore + OrgEvents *org_event.OrgEventstore } func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, eventstore eventstore.Eventstore, repos EventstoreRepos, systemDefaults sd.SystemDefaults, i18n *i18n.Translator, dir http.FileSystem) []spooler.Handler { @@ -37,7 +39,10 @@ func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, ev logging.Log("HANDL-s90ew").WithError(err).Debug("error create new aes crypto") } return []spooler.Handler{ - &NotifyUser{handler: handler{view, bulkLimit, configs.cycleDuration("User"), errorCount}}, + &NotifyUser{ + handler: handler{view, bulkLimit, configs.cycleDuration("User"), errorCount}, + orgEvents: repos.OrgEvents, + }, &Notification{ handler: handler{view, bulkLimit, configs.cycleDuration("Notification"), errorCount}, eventstore: eventstore, diff --git a/internal/notification/repository/eventsourcing/handler/notify_user.go b/internal/notification/repository/eventsourcing/handler/notify_user.go index 5371eff14b..bf1255ccc1 100644 --- a/internal/notification/repository/eventsourcing/handler/notify_user.go +++ b/internal/notification/repository/eventsourcing/handler/notify_user.go @@ -1,6 +1,11 @@ package handler import ( + "context" + es_models "github.com/caos/zitadel/internal/eventstore/models" + org_model "github.com/caos/zitadel/internal/org/model" + org_events "github.com/caos/zitadel/internal/org/repository/eventsourcing" + org_es_model "github.com/caos/zitadel/internal/org/repository/eventsourcing/model" es_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model" "time" @@ -9,13 +14,13 @@ import ( "github.com/caos/zitadel/internal/eventstore" "github.com/caos/zitadel/internal/eventstore/models" "github.com/caos/zitadel/internal/eventstore/spooler" - "github.com/caos/zitadel/internal/user/repository/eventsourcing" view_model "github.com/caos/zitadel/internal/user/repository/view/model" ) type NotifyUser struct { handler eventstore eventstore.Eventstore + orgEvents *org_events.OrgEventstore } const ( @@ -33,35 +38,131 @@ func (p *NotifyUser) EventQuery() (*models.SearchQuery, error) { if err != nil { return nil, err } - return eventsourcing.UserQuery(sequence), nil + return es_models.NewSearchQuery(). + AggregateTypeFilter(es_model.UserAggregate, org_es_model.OrgAggregate). + LatestSequenceFilter(sequence), nil } -func (p *NotifyUser) Reduce(event *models.Event) (err error) { +func (u *NotifyUser) Reduce(event *models.Event) (err error) { + switch event.AggregateType { + case es_model.UserAggregate: + return u.ProcessUser(event) + case org_es_model.OrgAggregate: + return u.ProcessOrg(event) + default: + return nil + } +} + +func (u *NotifyUser) ProcessUser(event *models.Event) (err error) { user := new(view_model.NotifyUser) switch event.Type { case es_model.UserAdded, es_model.UserRegistered: user.AppendEvent(event) + u.fillLoginNames(user) case es_model.UserProfileChanged, es_model.UserEmailChanged, es_model.UserEmailVerified, es_model.UserPhoneChanged, es_model.UserPhoneVerified, es_model.UserPhoneRemoved: - user, err = p.view.NotifyUserByID(event.AggregateID) + user, err = u.view.NotifyUserByID(event.AggregateID) if err != nil { return err } err = user.AppendEvent(event) case es_model.UserRemoved: - err = p.view.DeleteNotifyUser(event.AggregateID, event.Sequence) + err = u.view.DeleteNotifyUser(event.AggregateID, event.Sequence) default: - return p.view.ProcessedNotifyUserSequence(event.Sequence) + return u.view.ProcessedNotifyUserSequence(event.Sequence) } if err != nil { return err } - return p.view.PutNotifyUser(user) + return u.view.PutNotifyUser(user, user.Sequence) +} + +func (u *NotifyUser) ProcessOrg(event *models.Event) (err error) { + switch event.Type { + case org_es_model.OrgDomainVerified, + org_es_model.OrgDomainRemoved, + org_es_model.OrgIamPolicyAdded, + org_es_model.OrgIamPolicyChanged, + org_es_model.OrgIamPolicyRemoved: + return u.fillLoginNamesOnOrgUsers(event) + case org_es_model.OrgDomainPrimarySet: + return u.fillPreferredLoginNamesOnOrgUsers(event) + default: + return u.view.ProcessedNotifyUserSequence(event.Sequence) + } + if err != nil { + return err + } + return nil +} + +func (u *NotifyUser) fillLoginNamesOnOrgUsers(event *models.Event) error { + org, err := u.orgEvents.OrgByID(context.Background(), org_model.NewOrg(event.ResourceOwner)) + if err != nil { + return err + } + policy, err := u.orgEvents.GetOrgIamPolicy(context.Background(), event.ResourceOwner) + if err != nil { + return err + } + users, err := u.view.NotifyUsersByOrgID(event.AggregateID) + if err != nil { + return err + } + for _, user := range users { + user.SetLoginNames(policy, org.Domains) + err := u.view.PutNotifyUser(user, 0) + if err != nil { + return err + } + } + return nil +} + +func (u *NotifyUser) fillPreferredLoginNamesOnOrgUsers(event *models.Event) error { + org, err := u.orgEvents.OrgByID(context.Background(), org_model.NewOrg(event.ResourceOwner)) + if err != nil { + return err + } + policy, err := u.orgEvents.GetOrgIamPolicy(context.Background(), event.ResourceOwner) + if err != nil { + return err + } + if !policy.UserLoginMustBeDomain { + return nil + } + users, err := u.view.NotifyUsersByOrgID(event.AggregateID) + if err != nil { + return err + } + for _, user := range users { + user.PreferredLoginName = user.GenerateLoginName(org.GetPrimaryDomain().Domain, policy.UserLoginMustBeDomain) + err := u.view.PutNotifyUser(user, 0) + if err != nil { + return err + } + } + return nil +} + +func (u *NotifyUser) fillLoginNames(user *view_model.NotifyUser) (err error) { + org, err := u.orgEvents.OrgByID(context.Background(), org_model.NewOrg(user.ResourceOwner)) + if err != nil { + return err + } + policy, err := u.orgEvents.GetOrgIamPolicy(context.Background(), user.ResourceOwner) + if err != nil { + return err + } + user.SetLoginNames(policy, org.Domains) + user.PreferredLoginName = user.GenerateLoginName(org.GetPrimaryDomain().Domain, policy.UserLoginMustBeDomain) + return nil } func (p *NotifyUser) OnError(event *models.Event, err error) error { diff --git a/internal/notification/repository/eventsourcing/repository.go b/internal/notification/repository/eventsourcing/repository.go index 3ea0ad37c1..4eff5c3598 100644 --- a/internal/notification/repository/eventsourcing/repository.go +++ b/internal/notification/repository/eventsourcing/repository.go @@ -9,6 +9,7 @@ import ( "github.com/caos/zitadel/internal/notification/repository/eventsourcing/handler" "github.com/caos/zitadel/internal/notification/repository/eventsourcing/spooler" noti_view "github.com/caos/zitadel/internal/notification/repository/eventsourcing/view" + es_org "github.com/caos/zitadel/internal/org/repository/eventsourcing" es_usr "github.com/caos/zitadel/internal/user/repository/eventsourcing" "golang.org/x/text/language" "net/http" @@ -19,6 +20,7 @@ type Config struct { Eventstore es_int.Config View types.SQL Spooler spooler.SpoolerConfig + Domain string } type EsRepository struct { @@ -47,11 +49,13 @@ func Start(conf Config, dir http.FileSystem, systemDefaults sd.SystemDefaults) ( if err != nil { return nil, err } + org := es_org.StartOrg(es_org.OrgConfig{Eventstore: es, IAMDomain: conf.Domain}, systemDefaults) + i18n, err := i18n.NewTranslator(dir, i18n.TranslatorConfig{DefaultLanguage: conf.DefaultLanguage}) if err != nil { return nil, err } - eventstoreRepos := handler.EventstoreRepos{UserEvents: user} + eventstoreRepos := handler.EventstoreRepos{UserEvents: user, OrgEvents: org} spool := spooler.StartSpooler(conf.Spooler, es, view, sqlClient, eventstoreRepos, systemDefaults, i18n, dir) return &EsRepository{ diff --git a/internal/notification/repository/eventsourcing/spooler/spooler.go b/internal/notification/repository/eventsourcing/spooler/spooler.go index 9a84b55064..47f5fe4628 100644 --- a/internal/notification/repository/eventsourcing/spooler/spooler.go +++ b/internal/notification/repository/eventsourcing/spooler/spooler.go @@ -8,7 +8,6 @@ import ( "github.com/caos/zitadel/internal/i18n" "github.com/caos/zitadel/internal/notification/repository/eventsourcing/handler" "github.com/caos/zitadel/internal/notification/repository/eventsourcing/view" - usr_event "github.com/caos/zitadel/internal/user/repository/eventsourcing" "net/http" ) @@ -19,10 +18,6 @@ type SpoolerConfig struct { Handlers handler.Configs } -type EventstoreRepos struct { - UserEvents *usr_event.UserEventstore -} - func StartSpooler(c SpoolerConfig, es eventstore.Eventstore, view *view.View, sql *sql.DB, eventstoreRepos handler.EventstoreRepos, systemDefaults sd.SystemDefaults, i18n *i18n.Translator, dir http.FileSystem) *spooler.Spooler { spoolerConfig := spooler.Config{ Eventstore: es, diff --git a/internal/notification/repository/eventsourcing/view/notify_user.go b/internal/notification/repository/eventsourcing/view/notify_user.go index c5abff4fe0..205dbbdc6b 100644 --- a/internal/notification/repository/eventsourcing/view/notify_user.go +++ b/internal/notification/repository/eventsourcing/view/notify_user.go @@ -14,12 +14,19 @@ func (v *View) NotifyUserByID(userID string) (*model.NotifyUser, error) { return view.NotifyUserByID(v.Db, notifyUserTable, userID) } -func (v *View) PutNotifyUser(user *model.NotifyUser) error { +func (v *View) PutNotifyUser(user *model.NotifyUser, sequence uint64) error { err := view.PutNotifyUser(v.Db, notifyUserTable, user) if err != nil { return err } - return v.ProcessedNotifyUserSequence(user.Sequence) + if sequence != 0 { + return v.ProcessedNotifyUserSequence(sequence) + } + return nil +} + +func (v *View) NotifyUsersByOrgID(orgID string) ([]*model.NotifyUser, error) { + return view.NotifyUsersByOrgID(v.Db, notifyUserTable, orgID) } func (v *View) DeleteNotifyUser(userID string, eventSequence uint64) error { diff --git a/internal/notification/static/i18n/de.yaml b/internal/notification/static/i18n/de.yaml index fb7b73cd09..3fbab64176 100644 --- a/internal/notification/static/i18n/de.yaml +++ b/internal/notification/static/i18n/de.yaml @@ -3,7 +3,7 @@ InitCode: PreHeader: User initialisieren Subject: User initialisieren Greeting: Hallo {{.FirstName}} {{.LastName}}, - Text: Dieser Benutzer wurde soeben im Zitadel erstellt. Du kannst den Button unten verwenden, um die Initialisierung abzuschliesen. (Code {{.Code}}) Falls du dieses Mail nicht angefordert hast, kannst du es einfach ignorieren. + Text: Dieser Benutzer wurde soeben im Zitadel erstellt. Mit dem Benutzernamen {{.PreferredLoginName}} kannst du dich anmelden. Nutze den untenstehenden Button, um die Initialisierung abzuschliesen. (Code {{.Code}}) Falls du dieses Mail nicht angefordert hast, kannst du es einfach ignorieren. ButtonText: Initialisierung abschliessen PasswordReset: Title: Zitadel - Passwort zurücksetzen diff --git a/internal/notification/static/i18n/en.yaml b/internal/notification/static/i18n/en.yaml index f0c3079428..22c660a18a 100644 --- a/internal/notification/static/i18n/en.yaml +++ b/internal/notification/static/i18n/en.yaml @@ -3,7 +3,7 @@ InitCode: PreHeader: Initialize User Subject: Initialize User Greeting: Hello {{.FirstName}} {{.LastName}}, - Text: This user was created in Zitadel. Please click the button below to finish the initialization process. (Code {{.Code}}) If you didn't ask for this mail, please ignore it. + Text: This user was created in Zitadel. Use the username {{.PreferredLoginName}} to login. Please click the button below to finish the initialization process. (Code {{.Code}}) If you didn't ask for this mail, please ignore it. ButtonText: Finish initialization PasswordReset: Title: Zitadel - Reset password diff --git a/internal/notification/types/init_code.go b/internal/notification/types/init_code.go index 1726e999fb..704aac23e4 100644 --- a/internal/notification/types/init_code.go +++ b/internal/notification/types/init_code.go @@ -31,9 +31,10 @@ func SendUserInitCode(dir http.FileSystem, i18n *i18n.Translator, user *view_mod return err } var args = map[string]interface{}{ - "FirstName": user.FirstName, - "LastName": user.LastName, - "Code": codeString, + "FirstName": user.FirstName, + "LastName": user.LastName, + "Code": codeString, + "PreferredLoginName": user.PreferredLoginName, } systemDefaults.Notifications.TemplateData.InitCode.Translate(i18n, args, user.PreferredLanguage) initCodeData := &InitCodeEmailData{TemplateData: systemDefaults.Notifications.TemplateData.InitCode, URL: url} diff --git a/internal/user/model/notify_user.go b/internal/user/model/notify_user.go index 7635b50c0a..1f6f6cade0 100644 --- a/internal/user/model/notify_user.go +++ b/internal/user/model/notify_user.go @@ -6,23 +6,25 @@ import ( ) type NotifyUser struct { - ID string - CreationDate time.Time - ChangeDate time.Time - ResourceOwner string - UserName string - FirstName string - LastName string - NickName string - DisplayName string - PreferredLanguage string - Gender Gender - LastEmail string - VerifiedEmail string - LastPhone string - VerifiedPhone string - PasswordSet bool - Sequence uint64 + ID string + CreationDate time.Time + ChangeDate time.Time + ResourceOwner string + UserName string + PreferredLoginName string + LoginNames []string + FirstName string + LastName string + NickName string + DisplayName string + PreferredLanguage string + Gender Gender + LastEmail string + VerifiedEmail string + LastPhone string + VerifiedPhone string + PasswordSet bool + Sequence uint64 } type NotifyUserSearchRequest struct { @@ -36,8 +38,9 @@ type NotifyUserSearchRequest struct { type NotifyUserSearchKey int32 const ( - NotifyUserSearchKeyUnspecified UserSearchKey = iota + NotifyUserSearchKeyUnspecified NotifyUserSearchKey = iota NotifyUserSearchKeyUserID + NotifyUserSearchKeyResourceOwner ) type NotifyUserSearchQuery struct { diff --git a/internal/user/repository/view/model/notify_user.go b/internal/user/repository/view/model/notify_user.go index 92c199e8fd..f87aff7658 100644 --- a/internal/user/repository/view/model/notify_user.go +++ b/internal/user/repository/view/model/notify_user.go @@ -5,79 +5,108 @@ import ( "github.com/caos/logging" caos_errs "github.com/caos/zitadel/internal/errors" "github.com/caos/zitadel/internal/eventstore/models" + org_model "github.com/caos/zitadel/internal/org/model" "github.com/caos/zitadel/internal/user/model" es_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model" + "github.com/lib/pq" "time" ) const ( - NotifyUserKeyUserID = "id" + NotifyUserKeyUserID = "id" + NotifyUserKeyResourceOwner = "id" ) type NotifyUser struct { - ID string `json:"-" gorm:"column:id;primary_key"` - CreationDate time.Time `json:"-" gorm:"column:creation_date"` - ChangeDate time.Time `json:"-" gorm:"column:change_date"` - ResourceOwner string `json:"-" gorm:"column:resource_owner"` - UserName string `json:"userName" gorm:"column:user_name"` - FirstName string `json:"firstName" gorm:"column:first_name"` - LastName string `json:"lastName" gorm:"column:last_name"` - NickName string `json:"nickName" gorm:"column:nick_name"` - DisplayName string `json:"displayName" gorm:"column:display_name"` - PreferredLanguage string `json:"preferredLanguage" gorm:"column:preferred_language"` - Gender int32 `json:"gender" gorm:"column:gender"` - LastEmail string `json:"email" gorm:"column:last_email"` - VerifiedEmail string `json:"-" gorm:"column:verified_email"` - LastPhone string `json:"phone" gorm:"column:last_phone"` - VerifiedPhone string `json:"-" gorm:"column:verified_phone"` - PasswordSet bool `json:"-" gorm:"column:password_set"` - Sequence uint64 `json:"-" gorm:"column:sequence"` + ID string `json:"-" gorm:"column:id;primary_key"` + CreationDate time.Time `json:"-" gorm:"column:creation_date"` + ChangeDate time.Time `json:"-" gorm:"column:change_date"` + ResourceOwner string `json:"-" gorm:"column:resource_owner"` + UserName string `json:"userName" gorm:"column:user_name"` + LoginNames pq.StringArray `json:"-" gorm:"column:login_names"` + PreferredLoginName string `json:"-" gorm:"column:preferred_login_name"` + FirstName string `json:"firstName" gorm:"column:first_name"` + LastName string `json:"lastName" gorm:"column:last_name"` + NickName string `json:"nickName" gorm:"column:nick_name"` + DisplayName string `json:"displayName" gorm:"column:display_name"` + PreferredLanguage string `json:"preferredLanguage" gorm:"column:preferred_language"` + Gender int32 `json:"gender" gorm:"column:gender"` + LastEmail string `json:"email" gorm:"column:last_email"` + VerifiedEmail string `json:"-" gorm:"column:verified_email"` + LastPhone string `json:"phone" gorm:"column:last_phone"` + VerifiedPhone string `json:"-" gorm:"column:verified_phone"` + PasswordSet bool `json:"-" gorm:"column:password_set"` + Sequence uint64 `json:"-" gorm:"column:sequence"` } func NotifyUserFromModel(user *model.NotifyUser) *NotifyUser { return &NotifyUser{ - ID: user.ID, - ChangeDate: user.ChangeDate, - CreationDate: user.CreationDate, - ResourceOwner: user.ResourceOwner, - UserName: user.UserName, - FirstName: user.FirstName, - LastName: user.LastName, - NickName: user.NickName, - DisplayName: user.DisplayName, - PreferredLanguage: user.PreferredLanguage, - Gender: int32(user.Gender), - LastEmail: user.LastEmail, - VerifiedEmail: user.VerifiedEmail, - LastPhone: user.LastPhone, - VerifiedPhone: user.VerifiedPhone, - PasswordSet: user.PasswordSet, - Sequence: user.Sequence, + ID: user.ID, + ChangeDate: user.ChangeDate, + CreationDate: user.CreationDate, + ResourceOwner: user.ResourceOwner, + UserName: user.UserName, + LoginNames: user.LoginNames, + PreferredLoginName: user.PreferredLoginName, + FirstName: user.FirstName, + LastName: user.LastName, + NickName: user.NickName, + DisplayName: user.DisplayName, + PreferredLanguage: user.PreferredLanguage, + Gender: int32(user.Gender), + LastEmail: user.LastEmail, + VerifiedEmail: user.VerifiedEmail, + LastPhone: user.LastPhone, + VerifiedPhone: user.VerifiedPhone, + PasswordSet: user.PasswordSet, + Sequence: user.Sequence, } } func NotifyUserToModel(user *NotifyUser) *model.NotifyUser { return &model.NotifyUser{ - ID: user.ID, - ChangeDate: user.ChangeDate, - CreationDate: user.CreationDate, - ResourceOwner: user.ResourceOwner, - UserName: user.UserName, - FirstName: user.FirstName, - LastName: user.LastName, - NickName: user.NickName, - DisplayName: user.DisplayName, - PreferredLanguage: user.PreferredLanguage, - Gender: model.Gender(user.Gender), - LastEmail: user.LastEmail, - VerifiedEmail: user.VerifiedEmail, - LastPhone: user.LastPhone, - VerifiedPhone: user.VerifiedPhone, - PasswordSet: user.PasswordSet, - Sequence: user.Sequence, + ID: user.ID, + ChangeDate: user.ChangeDate, + CreationDate: user.CreationDate, + ResourceOwner: user.ResourceOwner, + UserName: user.UserName, + LoginNames: user.LoginNames, + PreferredLoginName: user.PreferredLoginName, + FirstName: user.FirstName, + LastName: user.LastName, + NickName: user.NickName, + DisplayName: user.DisplayName, + PreferredLanguage: user.PreferredLanguage, + Gender: model.Gender(user.Gender), + LastEmail: user.LastEmail, + VerifiedEmail: user.VerifiedEmail, + LastPhone: user.LastPhone, + VerifiedPhone: user.VerifiedPhone, + PasswordSet: user.PasswordSet, + Sequence: user.Sequence, } } +func (u *NotifyUser) GenerateLoginName(domain string, appendDomain bool) string { + if !appendDomain { + return u.UserName + } + return u.UserName + "@" + domain +} + +func (u *NotifyUser) SetLoginNames(policy *org_model.OrgIamPolicy, domains []*org_model.OrgDomain) { + loginNames := make([]string, 0) + for _, d := range domains { + if d.Verified { + loginNames = append(loginNames, u.GenerateLoginName(d.Domain, true)) + } + } + if !policy.UserLoginMustBeDomain { + loginNames = append(loginNames, u.UserName) + } + u.LoginNames = loginNames +} + func (u *NotifyUser) AppendEvent(event *models.Event) (err error) { u.ChangeDate = event.CreationDate u.Sequence = event.Sequence diff --git a/internal/user/repository/view/model/notify_user_query.go b/internal/user/repository/view/model/notify_user_query.go new file mode 100644 index 0000000000..d036e62200 --- /dev/null +++ b/internal/user/repository/view/model/notify_user_query.go @@ -0,0 +1,61 @@ +package model + +import ( + global_model "github.com/caos/zitadel/internal/model" + usr_model "github.com/caos/zitadel/internal/user/model" + "github.com/caos/zitadel/internal/view/repository" +) + +type NotifyUserSearchRequest usr_model.NotifyUserSearchRequest +type NotifyUserSearchQuery usr_model.NotifyUserSearchQuery +type NotifyUserSearchKey usr_model.NotifyUserSearchKey + +func (req NotifyUserSearchRequest) GetLimit() uint64 { + return req.Limit +} + +func (req NotifyUserSearchRequest) GetOffset() uint64 { + return req.Offset +} + +func (req NotifyUserSearchRequest) GetSortingColumn() repository.ColumnKey { + if req.SortingColumn == usr_model.NotifyUserSearchKeyUnspecified { + return nil + } + return NotifyUserSearchKey(req.SortingColumn) +} + +func (req NotifyUserSearchRequest) GetAsc() bool { + return req.Asc +} + +func (req NotifyUserSearchRequest) GetQueries() []repository.SearchQuery { + result := make([]repository.SearchQuery, len(req.Queries)) + for i, q := range req.Queries { + result[i] = NotifyUserSearchQuery{Key: q.Key, Value: q.Value, Method: q.Method} + } + return result +} + +func (req NotifyUserSearchQuery) GetKey() repository.ColumnKey { + return NotifyUserSearchKey(req.Key) +} + +func (req NotifyUserSearchQuery) GetMethod() global_model.SearchMethod { + return req.Method +} + +func (req NotifyUserSearchQuery) GetValue() interface{} { + return req.Value +} + +func (key NotifyUserSearchKey) ToColumnName() string { + switch usr_model.NotifyUserSearchKey(key) { + case usr_model.NotifyUserSearchKeyUserID: + return NotifyUserKeyUserID + case usr_model.NotifyUserSearchKeyResourceOwner: + return NotifyUserKeyResourceOwner + default: + return "" + } +} diff --git a/internal/user/repository/view/notify_user.go b/internal/user/repository/view/notify_user.go index d94c152d3e..62f61acf00 100644 --- a/internal/user/repository/view/notify_user.go +++ b/internal/user/repository/view/notify_user.go @@ -2,6 +2,7 @@ package view import ( caos_errs "github.com/caos/zitadel/internal/errors" + global_model "github.com/caos/zitadel/internal/model" usr_model "github.com/caos/zitadel/internal/user/model" "github.com/caos/zitadel/internal/user/repository/view/model" "github.com/caos/zitadel/internal/view/repository" @@ -10,7 +11,7 @@ import ( func NotifyUserByID(db *gorm.DB, table, userID string) (*model.NotifyUser, error) { user := new(model.NotifyUser) - query := repository.PrepareGetByKey(table, model.UserSearchKey(usr_model.NotifyUserSearchKeyUserID), userID) + query := repository.PrepareGetByKey(table, model.NotifyUserSearchKey(usr_model.NotifyUserSearchKeyUserID), userID) err := query(db, user) if caos_errs.IsNotFound(err) { return nil, caos_errs.ThrowNotFound(nil, "VIEW-Gad31", "Errors.User.NotFound") @@ -18,6 +19,20 @@ func NotifyUserByID(db *gorm.DB, table, userID string) (*model.NotifyUser, error return user, err } +func NotifyUsersByOrgID(db *gorm.DB, table, orgID string) ([]*model.NotifyUser, error) { + users := make([]*model.NotifyUser, 0) + orgIDQuery := &usr_model.NotifyUserSearchQuery{ + Key: usr_model.NotifyUserSearchKeyResourceOwner, + Method: global_model.SearchMethodEquals, + Value: orgID, + } + query := repository.PrepareSearchQuery(table, model.NotifyUserSearchRequest{ + Queries: []*usr_model.NotifyUserSearchQuery{orgIDQuery}, + }) + _, err := query(db, &users) + return users, err +} + func PutNotifyUser(db *gorm.DB, table string, project *model.NotifyUser) error { save := repository.PrepareSave(table) return save(db, project) diff --git a/migrations/cockroach/V1.3__notification_user_loginnames.sql b/migrations/cockroach/V1.3__notification_user_loginnames.sql new file mode 100644 index 0000000000..0ee8651f73 --- /dev/null +++ b/migrations/cockroach/V1.3__notification_user_loginnames.sql @@ -0,0 +1,6 @@ +BEGIN; + +ALTER TABLE notification.notify_users ADD COLUMN login_names TEXT ARRAY; +ALTER TABLE notification.notify_users ADD COLUMN preferred_login_name TEXT; + +COMMIT; \ No newline at end of file