From d016379e2a2383a994ce44491caafe5a76037e20 Mon Sep 17 00:00:00 2001 From: Livio Spring Date: Wed, 24 Apr 2024 17:50:58 +0200 Subject: [PATCH] feat: pass and handle auth request context for email links (#7815) * pass and handle auth request context * tests and cleanup * cleanup --- cmd/setup/config.go | 2 + internal/api/grpc/auth/email.go | 2 +- internal/api/grpc/management/user.go | 6 +- internal/api/ui/login/auth_request.go | 22 + .../api/ui/login/init_password_handler.go | 16 +- internal/api/ui/login/init_user_handler.go | 18 +- internal/api/ui/login/mail_verify_handler.go | 18 +- .../api/ui/login/password_reset_handler.go | 2 +- internal/api/ui/login/register_handler.go | 35 +- .../eventsourcing/eventstore/auth_request.go | 24 +- .../eventsourcing/handler/user_session.go | 63 +- internal/command/org_test.go | 1 + internal/command/user_human.go | 192 +-- internal/command/user_human_email.go | 9 +- internal/command/user_human_email_model.go | 2 + internal/command/user_human_email_test.go | 155 +- internal/command/user_human_init.go | 7 +- internal/command/user_human_init_model.go | 2 + internal/command/user_human_init_test.go | 195 ++- internal/command/user_human_password.go | 4 +- internal/command/user_human_password_test.go | 188 ++- internal/command/user_human_test.go | 1504 +---------------- internal/command/user_test.go | 1 + internal/command/user_v2_email_test.go | 12 +- internal/command/user_v2_human.go | 7 +- internal/command/user_v2_human_test.go | 9 + internal/command/user_v2_model_test.go | 3 + internal/command/user_v2_password_test.go | 9 +- internal/command/user_v2_test.go | 134 +- .../notification/handlers/user_notifier.go | 6 +- .../handlers/user_notifier_test.go | 137 +- .../types/email_verification_code.go | 4 +- .../types/email_verification_code_test.go | 34 +- internal/notification/types/init_code.go | 4 +- internal/notification/types/password_code.go | 4 +- internal/repository/user/human.go | 7 + internal/repository/user/human_email.go | 15 +- internal/repository/user/human_password.go | 16 +- 38 files changed, 851 insertions(+), 2018 deletions(-) diff --git a/cmd/setup/config.go b/cmd/setup/config.go index f5547d21c..00ab64e2b 100644 --- a/cmd/setup/config.go +++ b/cmd/setup/config.go @@ -18,6 +18,7 @@ import ( "github.com/zitadel/zitadel/internal/command" "github.com/zitadel/zitadel/internal/config/hook" "github.com/zitadel/zitadel/internal/config/systemdefaults" + "github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/database" "github.com/zitadel/zitadel/internal/eventstore" "github.com/zitadel/zitadel/internal/id" @@ -69,6 +70,7 @@ func MustNewConfig(v *viper.Viper) *Config { hook.EnumHookFunc(authz.MemberTypeString), actions.HTTPConfigDecodeHook, hooks.MapTypeStringDecode[string, *authz.SystemAPIUser], + hooks.MapTypeStringDecode[string, crypto.HasherConfig], hooks.SliceTypeStringDecode[authz.RoleMapping], )), ) diff --git a/internal/api/grpc/auth/email.go b/internal/api/grpc/auth/email.go index 3e4f636c7..c6153546e 100644 --- a/internal/api/grpc/auth/email.go +++ b/internal/api/grpc/auth/email.go @@ -65,7 +65,7 @@ func (s *Server) ResendMyEmailVerification(ctx context.Context, _ *auth_pb.Resen if err != nil { return nil, err } - objectDetails, err := s.command.CreateHumanEmailVerificationCode(ctx, ctxData.UserID, ctxData.ResourceOwner, emailCodeGenerator) + objectDetails, err := s.command.CreateHumanEmailVerificationCode(ctx, ctxData.UserID, ctxData.ResourceOwner, emailCodeGenerator, "") if err != nil { return nil, err } diff --git a/internal/api/grpc/management/user.go b/internal/api/grpc/management/user.go index 6166bf4a5..f662b8c59 100644 --- a/internal/api/grpc/management/user.go +++ b/internal/api/grpc/management/user.go @@ -473,7 +473,7 @@ func (s *Server) ResendHumanInitialization(ctx context.Context, req *mgmt_pb.Res if err != nil { return nil, err } - details, err := s.command.ResendInitialMail(ctx, req.UserId, domain.EmailAddress(req.Email), authz.GetCtxData(ctx).OrgID, initCodeGenerator) + details, err := s.command.ResendInitialMail(ctx, req.UserId, domain.EmailAddress(req.Email), authz.GetCtxData(ctx).OrgID, initCodeGenerator, "") if err != nil { return nil, err } @@ -487,7 +487,7 @@ func (s *Server) ResendHumanEmailVerification(ctx context.Context, req *mgmt_pb. if err != nil { return nil, err } - objectDetails, err := s.command.CreateHumanEmailVerificationCode(ctx, req.UserId, authz.GetCtxData(ctx).OrgID, emailCodeGenerator) + objectDetails, err := s.command.CreateHumanEmailVerificationCode(ctx, req.UserId, authz.GetCtxData(ctx).OrgID, emailCodeGenerator, "") if err != nil { return nil, err } @@ -590,7 +590,7 @@ func (s *Server) SendHumanResetPasswordNotification(ctx context.Context, req *mg if err != nil { return nil, err } - objectDetails, err := s.command.RequestSetPassword(ctx, req.UserId, authz.GetCtxData(ctx).OrgID, notifyTypeToDomain(req.Type), passwordCodeGenerator) + objectDetails, err := s.command.RequestSetPassword(ctx, req.UserId, authz.GetCtxData(ctx).OrgID, notifyTypeToDomain(req.Type), passwordCodeGenerator, "") if err != nil { return nil, err } diff --git a/internal/api/ui/login/auth_request.go b/internal/api/ui/login/auth_request.go index 15a39b0d8..f2dcce57d 100644 --- a/internal/api/ui/login/auth_request.go +++ b/internal/api/ui/login/auth_request.go @@ -3,6 +3,8 @@ package login import ( "net/http" + "github.com/zitadel/logging" + http_mw "github.com/zitadel/zitadel/internal/api/http/middleware" "github.com/zitadel/zitadel/internal/domain" ) @@ -33,3 +35,23 @@ func (l *Login) getAuthRequestAndParseData(r *http.Request, data interface{}) (* func (l *Login) getParseData(r *http.Request, data interface{}) error { return l.parser.Parse(r, data) } + +// checkOptionalAuthRequestOfEmailLinks tries to get the [domain.AuthRequest] from the request. +// In case any error occurs, e.g. if the user agent does not correspond, the `authRequestID` query parameter will be +// removed from the request URL and form to ensure subsequent functions and pages do not use it. +// This function is used for handling links in emails, which could possibly be opened on another device than the +// auth request was initiated. +func (l *Login) checkOptionalAuthRequestOfEmailLinks(r *http.Request) *domain.AuthRequest { + authReq, err := l.getAuthRequest(r) + if err == nil { + return authReq + } + logging.WithError(err).Infof("authrequest could not be found for email link on path %s", r.URL.RequestURI()) + queries := r.URL.Query() + queries.Del(QueryAuthRequestID) + r.URL.RawQuery = queries.Encode() + r.RequestURI = r.URL.RequestURI() + r.Form.Del(QueryAuthRequestID) + r.PostForm.Del(QueryAuthRequestID) + return nil +} diff --git a/internal/api/ui/login/init_password_handler.go b/internal/api/ui/login/init_password_handler.go index db2518c43..37fb9470c 100644 --- a/internal/api/ui/login/init_password_handler.go +++ b/internal/api/ui/login/init_password_handler.go @@ -1,8 +1,8 @@ package login import ( - "fmt" "net/http" + "net/url" http_mw "github.com/zitadel/zitadel/internal/api/http/middleware" "github.com/zitadel/zitadel/internal/domain" @@ -38,14 +38,20 @@ type initPasswordData struct { HasSymbol string } -func InitPasswordLink(origin, userID, code, orgID string) string { - return fmt.Sprintf("%s%s?userID=%s&code=%s&orgID=%s", externalLink(origin), EndpointInitPassword, userID, code, orgID) +func InitPasswordLink(origin, userID, code, orgID, authRequestID string) string { + v := url.Values{} + v.Set(queryInitPWUserID, userID) + v.Set(queryInitPWCode, code) + v.Set(queryOrgID, orgID) + v.Set(QueryAuthRequestID, authRequestID) + return externalLink(origin) + EndpointInitPassword + "?" + v.Encode() } func (l *Login) handleInitPassword(w http.ResponseWriter, r *http.Request) { + authReq := l.checkOptionalAuthRequestOfEmailLinks(r) userID := r.FormValue(queryInitPWUserID) code := r.FormValue(queryInitPWCode) - l.renderInitPassword(w, r, nil, userID, code, nil) + l.renderInitPassword(w, r, authReq, userID, code, nil) } func (l *Login) handleInitPasswordCheck(w http.ResponseWriter, r *http.Request) { @@ -94,7 +100,7 @@ func (l *Login) resendPasswordSet(w http.ResponseWriter, r *http.Request, authRe l.renderInitPassword(w, r, authReq, userID, "", err) return } - _, err = l.command.RequestSetPassword(setContext(r.Context(), userOrg), userID, userOrg, domain.NotificationTypeEmail, passwordCodeGenerator) + _, err = l.command.RequestSetPassword(setContext(r.Context(), userOrg), userID, userOrg, domain.NotificationTypeEmail, passwordCodeGenerator, authReq.ID) l.renderInitPassword(w, r, authReq, userID, "", err) } diff --git a/internal/api/ui/login/init_user_handler.go b/internal/api/ui/login/init_user_handler.go index 119460a8a..fe01447c4 100644 --- a/internal/api/ui/login/init_user_handler.go +++ b/internal/api/ui/login/init_user_handler.go @@ -1,8 +1,8 @@ package login import ( - "fmt" "net/http" + "net/url" "strconv" http_mw "github.com/zitadel/zitadel/internal/api/http/middleware" @@ -44,16 +44,24 @@ type initUserData struct { HasSymbol string } -func InitUserLink(origin, userID, loginName, code, orgID string, passwordSet bool) string { - return fmt.Sprintf("%s%s?userID=%s&loginname=%s&code=%s&orgID=%s&passwordset=%t", externalLink(origin), EndpointInitUser, userID, loginName, code, orgID, passwordSet) +func InitUserLink(origin, userID, loginName, code, orgID string, passwordSet bool, authRequestID string) string { + v := url.Values{} + v.Set(queryInitUserUserID, userID) + v.Set(queryInitUserLoginName, loginName) + v.Set(queryInitUserCode, code) + v.Set(queryOrgID, orgID) + v.Set(queryInitUserPassword, strconv.FormatBool(passwordSet)) + v.Set(QueryAuthRequestID, authRequestID) + return externalLink(origin) + EndpointInitUser + "?" + v.Encode() } func (l *Login) handleInitUser(w http.ResponseWriter, r *http.Request) { + authReq := l.checkOptionalAuthRequestOfEmailLinks(r) userID := r.FormValue(queryInitUserUserID) code := r.FormValue(queryInitUserCode) loginName := r.FormValue(queryInitUserLoginName) passwordSet, _ := strconv.ParseBool(r.FormValue(queryInitUserPassword)) - l.renderInitUser(w, r, nil, userID, loginName, code, passwordSet, nil) + l.renderInitUser(w, r, authReq, userID, loginName, code, passwordSet, nil) } func (l *Login) handleInitUserCheck(w http.ResponseWriter, r *http.Request) { @@ -105,7 +113,7 @@ func (l *Login) resendUserInit(w http.ResponseWriter, r *http.Request, authReq * l.renderInitUser(w, r, authReq, userID, loginName, "", showPassword, err) return } - _, err = l.command.ResendInitialMail(setContext(r.Context(), userOrgID), userID, "", userOrgID, initCodeGenerator) + _, err = l.command.ResendInitialMail(setContext(r.Context(), userOrgID), userID, "", userOrgID, initCodeGenerator, authReq.ID) l.renderInitUser(w, r, authReq, userID, loginName, "", showPassword, err) } diff --git a/internal/api/ui/login/mail_verify_handler.go b/internal/api/ui/login/mail_verify_handler.go index 50f03df81..327b8a118 100644 --- a/internal/api/ui/login/mail_verify_handler.go +++ b/internal/api/ui/login/mail_verify_handler.go @@ -1,8 +1,8 @@ package login import ( - "fmt" "net/http" + "net/url" "github.com/zitadel/zitadel/internal/domain" ) @@ -27,18 +27,24 @@ type mailVerificationData struct { UserID string } -func MailVerificationLink(origin, userID, code, orgID string) string { - return fmt.Sprintf("%s%s?userID=%s&code=%s&orgID=%s", externalLink(origin), EndpointMailVerification, userID, code, orgID) +func MailVerificationLink(origin, userID, code, orgID, authRequestID string) string { + v := url.Values{} + v.Set(queryUserID, userID) + v.Set(queryCode, code) + v.Set(queryOrgID, orgID) + v.Set(QueryAuthRequestID, authRequestID) + return externalLink(origin) + EndpointMailVerification + "?" + v.Encode() } func (l *Login) handleMailVerification(w http.ResponseWriter, r *http.Request) { + authReq := l.checkOptionalAuthRequestOfEmailLinks(r) userID := r.FormValue(queryUserID) code := r.FormValue(queryCode) if code != "" { - l.checkMailCode(w, r, nil, userID, code) + l.checkMailCode(w, r, authReq, userID, code) return } - l.renderMailVerification(w, r, nil, userID, nil) + l.renderMailVerification(w, r, authReq, userID, nil) } func (l *Login) handleMailVerificationCheck(w http.ResponseWriter, r *http.Request) { @@ -61,7 +67,7 @@ func (l *Login) handleMailVerificationCheck(w http.ResponseWriter, r *http.Reque l.checkMailCode(w, r, authReq, data.UserID, data.Code) return } - _, err = l.command.CreateHumanEmailVerificationCode(setContext(r.Context(), userOrg), data.UserID, userOrg, emailCodeGenerator) + _, err = l.command.CreateHumanEmailVerificationCode(setContext(r.Context(), userOrg), data.UserID, userOrg, emailCodeGenerator, authReq.ID) l.renderMailVerification(w, r, authReq, data.UserID, err) } diff --git a/internal/api/ui/login/password_reset_handler.go b/internal/api/ui/login/password_reset_handler.go index ad5782db5..ff57321e9 100644 --- a/internal/api/ui/login/password_reset_handler.go +++ b/internal/api/ui/login/password_reset_handler.go @@ -33,7 +33,7 @@ func (l *Login) handlePasswordReset(w http.ResponseWriter, r *http.Request) { l.renderPasswordResetDone(w, r, authReq, err) return } - _, err = l.command.RequestSetPassword(setContext(r.Context(), authReq.UserOrgID), user.ID, authReq.UserOrgID, domain.NotificationTypeEmail, passwordCodeGenerator) + _, err = l.command.RequestSetPassword(setContext(r.Context(), authReq.UserOrgID), user.ID, authReq.UserOrgID, domain.NotificationTypeEmail, passwordCodeGenerator, authReq.ID) l.renderPasswordResetDone(w, r, authReq, err) } diff --git a/internal/api/ui/login/register_handler.go b/internal/api/ui/login/register_handler.go index 8fed0d46e..d487903a8 100644 --- a/internal/api/ui/login/register_handler.go +++ b/internal/api/ui/login/register_handler.go @@ -7,6 +7,7 @@ import ( "github.com/zitadel/zitadel/internal/api/authz" http_mw "github.com/zitadel/zitadel/internal/api/http/middleware" + "github.com/zitadel/zitadel/internal/command" "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/zerrors" ) @@ -67,22 +68,6 @@ func (l *Login) handleRegisterCheck(w http.ResponseWriter, r *http.Request) { if authRequest != nil && authRequest.RequestedOrgID != "" && authRequest.RequestedOrgID != resourceOwner { resourceOwner = authRequest.RequestedOrgID } - initCodeGenerator, err := l.query.InitEncryptionGenerator(r.Context(), domain.SecretGeneratorTypeInitCode, l.userCodeAlg) - if err != nil { - l.renderRegister(w, r, authRequest, data, err) - return - } - emailCodeGenerator, err := l.query.InitEncryptionGenerator(r.Context(), domain.SecretGeneratorTypeVerifyEmailCode, l.userCodeAlg) - if err != nil { - l.renderRegister(w, r, authRequest, data, err) - return - } - phoneCodeGenerator, err := l.query.InitEncryptionGenerator(r.Context(), domain.SecretGeneratorTypeVerifyPhoneCode, l.userCodeAlg) - if err != nil { - l.renderRegister(w, r, authRequest, data, err) - return - } - // For consistency with the external authentication flow, // the setMetadata() function is provided on the pre creation hook, for now, // like for the ExternalAuthentication flow. @@ -96,22 +81,14 @@ func (l *Login) handleRegisterCheck(w http.ResponseWriter, r *http.Request) { l.renderRegister(w, r, authRequest, data, err) return } - user, err = l.command.RegisterHuman(setContext(r.Context(), resourceOwner), resourceOwner, user, nil, nil, initCodeGenerator, emailCodeGenerator, phoneCodeGenerator) + + human := command.AddHumanFromDomain(user, metadatas, authRequest, nil) + err = l.command.AddUserHuman(setContext(r.Context(), resourceOwner), resourceOwner, human, true, l.userCodeAlg) if err != nil { l.renderRegister(w, r, authRequest, data, err) return } - - if len(metadatas) > 0 { - _, err = l.command.BulkSetUserMetadata(r.Context(), user.AggregateID, resourceOwner, metadatas...) - if err != nil { - // TODO: What if action is configured to be allowed to fail? Same question for external registration. - l.renderRegister(w, r, authRequest, data, err) - return - } - } - - userGrants, err := l.runPostCreationActions(user.AggregateID, authRequest, r, resourceOwner, domain.FlowTypeInternalAuthentication) + userGrants, err := l.runPostCreationActions(human.ID, authRequest, r, resourceOwner, domain.FlowTypeInternalAuthentication) if err != nil { l.renderError(w, r, authRequest, err) return @@ -128,7 +105,7 @@ func (l *Login) handleRegisterCheck(w http.ResponseWriter, r *http.Request) { return } userAgentID, _ := http_mw.UserAgentIDFromCtx(r.Context()) - err = l.authRepo.SelectUser(r.Context(), authRequest.ID, user.AggregateID, userAgentID) + err = l.authRepo.SelectUser(r.Context(), authRequest.ID, human.ID, userAgentID) if err != nil { l.renderRegister(w, r, authRequest, data, err) return diff --git a/internal/auth/repository/eventsourcing/eventstore/auth_request.go b/internal/auth/repository/eventsourcing/eventstore/auth_request.go index eea29a167..1d0446249 100644 --- a/internal/auth/repository/eventsourcing/eventstore/auth_request.go +++ b/internal/auth/repository/eventsourcing/eventstore/auth_request.go @@ -543,23 +543,19 @@ func (repo *AuthRequestRepo) AutoRegisterExternalUser(ctx context.Context, regis if err != nil { return err } - initCodeGenerator, err := repo.Query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypeInitCode, repo.UserCodeAlg) + addMetadata := make([]*command.AddMetadataEntry, len(metadatas)) + for i, metadata := range metadatas { + addMetadata[i] = &command.AddMetadataEntry{ + Key: metadata.Key, + Value: metadata.Value, + } + } + human := command.AddHumanFromDomain(registerUser, metadatas, request, externalIDP) + err = repo.Command.AddUserHuman(ctx, resourceOwner, human, true, repo.UserCodeAlg) if err != nil { return err } - emailCodeGenerator, err := repo.Query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypeVerifyEmailCode, repo.UserCodeAlg) - if err != nil { - return err - } - phoneCodeGenerator, err := repo.Query.InitEncryptionGenerator(ctx, domain.SecretGeneratorTypeVerifyPhoneCode, repo.UserCodeAlg) - if err != nil { - return err - } - human, err := repo.Command.RegisterHuman(ctx, resourceOwner, registerUser, externalIDP, orgMemberRoles, initCodeGenerator, emailCodeGenerator, phoneCodeGenerator) - if err != nil { - return err - } - request.SetUserInfo(human.AggregateID, human.Username, human.PreferredLoginName, human.DisplayName, "", human.ResourceOwner) + request.SetUserInfo(human.ID, human.Username, human.Username, human.DisplayName, "", resourceOwner) request.SelectedIDPConfigID = externalIDP.IDPConfigID request.LinkingUsers = nil err = repo.Command.UserIDPLoginChecked(ctx, request.UserOrgID, request.UserID, request.WithCurrentInfo(info)) diff --git a/internal/auth/repository/eventsourcing/handler/user_session.go b/internal/auth/repository/eventsourcing/handler/user_session.go index 12cb98f9d..fc2285684 100644 --- a/internal/auth/repository/eventsourcing/handler/user_session.go +++ b/internal/auth/repository/eventsourcing/handler/user_session.go @@ -160,6 +160,10 @@ func (s *UserSession) Reducers() []handler.AggregateReducer { Event: user.UserRemovedType, Reduce: s.Reduce, }, + { + Event: user.HumanRegisteredType, + Reduce: s.Reduce, + }, }, }, { @@ -234,6 +238,8 @@ func (u *UserSession) Reduce(event eventstore.Event) (_ *handler.Statement, err handler.NewCol("multi_factor_verification_type", domain.MFALevelNotSetUp), handler.NewCol("external_login_verification", time.Time{}), handler.NewCol("state", domain.UserSessionStateTerminated), + handler.NewCol("change_date", event.CreatedAt()), + handler.NewCol("sequence", event.Sequence()), }, []handler.Condition{ handler.NewCond("instance_id", event.Aggregate().InstanceID), @@ -247,16 +253,30 @@ func (u *UserSession) Reduce(event eventstore.Event) (_ *handler.Statement, err if err != nil { return nil, err } - return handler.NewUpdateStatement(event, - []handler.Column{ - handler.NewCol("password_verification", time.Time{}), - }, - []handler.Condition{ - handler.NewCond("instance_id", event.Aggregate().InstanceID), - handler.NewCond("user_id", event.Aggregate().ID), - handler.Not(handler.NewCond("user_agent_id", userAgent)), - handler.Not(handler.NewCond("state", domain.UserSessionStateTerminated)), - }, + return handler.NewMultiStatement(event, + handler.AddUpdateStatement( + []handler.Column{ + handler.NewCol("password_verification", event.CreatedAt()), + handler.NewCol("change_date", event.CreatedAt()), + handler.NewCol("sequence", event.Sequence()), + }, + []handler.Condition{ + handler.NewCond("instance_id", event.Aggregate().InstanceID), + handler.NewCond("user_id", event.Aggregate().ID), + handler.NewCond("user_agent_id", userAgent), + }), + handler.AddUpdateStatement( + []handler.Column{ + handler.NewCol("password_verification", time.Time{}), + handler.NewCol("change_date", event.CreatedAt()), + handler.NewCol("sequence", event.Sequence()), + }, + []handler.Condition{ + handler.NewCond("instance_id", event.Aggregate().InstanceID), + handler.NewCond("user_id", event.Aggregate().ID), + handler.Not(handler.NewCond("user_agent_id", userAgent)), + handler.Not(handler.NewCond("state", domain.UserSessionStateTerminated)), + }), ), nil case user.UserV1MFAOTPRemovedType, user.HumanMFAOTPRemovedType, @@ -264,6 +284,8 @@ func (u *UserSession) Reduce(event eventstore.Event) (_ *handler.Statement, err return handler.NewUpdateStatement(event, []handler.Column{ handler.NewCol("second_factor_verification", time.Time{}), + handler.NewCol("change_date", event.CreatedAt()), + handler.NewCol("sequence", event.Sequence()), }, []handler.Condition{ handler.NewCond("instance_id", event.Aggregate().InstanceID), @@ -277,6 +299,8 @@ func (u *UserSession) Reduce(event eventstore.Event) (_ *handler.Statement, err []handler.Column{ handler.NewCol("external_login_verification", time.Time{}), handler.NewCol("selected_idp_config_id", ""), + handler.NewCol("change_date", event.CreatedAt()), + handler.NewCol("sequence", event.Sequence()), }, []handler.Condition{ handler.NewCond("instance_id", event.Aggregate().InstanceID), @@ -289,6 +313,8 @@ func (u *UserSession) Reduce(event eventstore.Event) (_ *handler.Statement, err []handler.Column{ handler.NewCol("passwordless_verification", time.Time{}), handler.NewCol("multi_factor_verification", time.Time{}), + handler.NewCol("change_date", event.CreatedAt()), + handler.NewCol("sequence", event.Sequence()), }, []handler.Condition{ handler.NewCond("instance_id", event.Aggregate().InstanceID), @@ -300,6 +326,23 @@ func (u *UserSession) Reduce(event eventstore.Event) (_ *handler.Statement, err return handler.NewStatement(event, func(ex handler.Executer, projectionName string) error { return u.view.DeleteUserSessions(event.Aggregate().ID, event.Aggregate().InstanceID) }), nil + case user.HumanRegisteredType: + return handler.NewStatement(event, func(ex handler.Executer, projectionName string) error { + eventData, err := view_model.UserSessionFromEvent(event) + if err != nil { + return err + } + session := &view_model.UserSessionView{ + CreationDate: event.CreatedAt(), + ResourceOwner: event.Aggregate().ResourceOwner, + UserAgentID: eventData.UserAgentID, + UserID: event.Aggregate().ID, + State: int32(domain.UserSessionStateActive), + InstanceID: event.Aggregate().InstanceID, + PasswordVerification: event.CreatedAt(), + } + return u.updateSession(session, event) + }), nil case instance.InstanceRemovedEventType: return handler.NewStatement(event, func(ex handler.Executer, projectionName string) error { return u.view.DeleteInstanceUserSessions(event.Aggregate().InstanceID) diff --git a/internal/command/org_test.go b/internal/command/org_test.go index 919c86b80..5c69034b5 100644 --- a/internal/command/org_test.go +++ b/internal/command/org_test.go @@ -1427,6 +1427,7 @@ func TestCommandSide_SetUpOrg(t *testing.T) { Crypted: []byte("userinit"), }, 1*time.Hour, + "", ), ), eventFromEventPusher(org.NewMemberAddedEvent(context.Background(), diff --git a/internal/command/user_human.go b/internal/command/user_human.go index a0cfff5bd..bfceb6ffc 100644 --- a/internal/command/user_human.go +++ b/internal/command/user_human.go @@ -10,7 +10,6 @@ import ( "github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/eventstore" - "github.com/zitadel/zitadel/internal/eventstore/v1/models" "github.com/zitadel/zitadel/internal/repository/user" "github.com/zitadel/zitadel/internal/telemetry/tracing" "github.com/zitadel/zitadel/internal/zerrors" @@ -57,7 +56,13 @@ type AddHuman struct { Passwordless bool ExternalIDP bool Register bool - Metadata []*AddMetadataEntry + // UserAgentID is optional and can be passed in case the user registered themselves. + // This will be used in the login UI to handle authentication automatically. + UserAgentID string + // AuthRequestID is optional and can be passed in case the user registered themselves. + // This will be used to pass the information in notifications for links to the login UI. + AuthRequestID string + Metadata []*AddMetadataEntry // Links are optional Links []*AddLink @@ -200,6 +205,7 @@ func (c *Commands) AddHumanCommand(human *AddHuman, orgID string, hasher *crypto human.Gender, human.Email.Address, domainPolicy.UserLoginMustBeDomain, + "", // no user agent id available ) } else { createCmd = user.NewHumanAddedEvent( @@ -272,7 +278,7 @@ func (c *Commands) addHumanCommandEmail(ctx context.Context, filter preparation. if err != nil { return nil, err } - return append(cmds, user.NewHumanInitialCodeAddedEvent(ctx, &a.Aggregate, initCode.Crypted, initCode.Expiry)), nil + return append(cmds, user.NewHumanInitialCodeAddedEvent(ctx, &a.Aggregate, initCode.Crypted, initCode.Expiry, human.AuthRequestID)), nil } if !human.Email.Verified { emailCode, err := c.newEmailCode(ctx, filter, codeAlg) @@ -460,61 +466,6 @@ func (c *Commands) ImportHuman(ctx context.Context, orgID string, human *domain. return writeModelToHuman(addedHuman), passwordlessCode, nil } -// Deprecated: use commands.AddUserHuman -func (c *Commands) RegisterHuman(ctx context.Context, orgID string, human *domain.Human, link *domain.UserIDPLink, orgMemberRoles []string, initCodeGenerator, emailCodeGenerator, phoneCodeGenerator crypto.Generator) (*domain.Human, error) { - if orgID == "" { - return nil, zerrors.ThrowInvalidArgument(nil, "COMMAND-GEdf2", "Errors.ResourceOwnerMissing") - } - domainPolicy, err := c.getOrgDomainPolicy(ctx, orgID) - if err != nil { - return nil, zerrors.ThrowPreconditionFailed(err, "COMMAND-33M9f", "Errors.Org.DomainPolicy.NotFound") - } - pwPolicy, err := c.getOrgPasswordComplexityPolicy(ctx, orgID) - if err != nil { - return nil, zerrors.ThrowPreconditionFailed(err, "COMMAND-M5Fsd", "Errors.Org.PasswordComplexityPolicy.NotFound") - } - loginPolicy, err := c.getOrgLoginPolicy(ctx, orgID) - if err != nil { - return nil, zerrors.ThrowPreconditionFailed(err, "COMMAND-Dfg3g", "Errors.Org.LoginPolicy.NotFound") - } - // check only if local registration is allowed, the idp will be checked separately - if !loginPolicy.AllowRegister && link == nil { - return nil, zerrors.ThrowPreconditionFailed(err, "COMMAND-SAbr3", "Errors.Org.LoginPolicy.RegistrationNotAllowed") - } - userEvents, registeredHuman, err := c.registerHuman(ctx, orgID, human, link, domainPolicy, pwPolicy, initCodeGenerator, emailCodeGenerator, phoneCodeGenerator) - if err != nil { - return nil, err - } - - orgMemberWriteModel := NewOrgMemberWriteModel(orgID, registeredHuman.AggregateID) - orgAgg := OrgAggregateFromWriteModel(&orgMemberWriteModel.WriteModel) - if len(orgMemberRoles) > 0 { - orgMember := &domain.Member{ - ObjectRoot: models.ObjectRoot{ - AggregateID: orgID, - }, - UserID: human.AggregateID, - Roles: orgMemberRoles, - } - memberEvent, err := c.addOrgMember(ctx, orgAgg, orgMemberWriteModel, orgMember) - if err != nil { - return nil, err - } - userEvents = append(userEvents, memberEvent) - } - - pushedEvents, err := c.eventstore.Push(ctx, userEvents...) - if err != nil { - return nil, err - } - - err = AppendAndReduce(registeredHuman, pushedEvents...) - if err != nil { - return nil, err - } - return writeModelToHuman(registeredHuman), nil -} - func (c *Commands) importHuman(ctx context.Context, orgID string, human *domain.Human, passwordless bool, links []*domain.UserIDPLink, domainPolicy *domain.DomainPolicy, pwPolicy *domain.PasswordComplexityPolicy, initCodeGenerator, emailCodeGenerator, phoneCodeGenerator, passwordlessCodeGenerator crypto.Generator) (events []eventstore.Command, humanWriteModel *HumanWriteModel, passwordlessCodeWriteModel *HumanPasswordlessInitCodeWriteModel, code string, err error) { if orgID == "" { return nil, nil, nil, "", zerrors.ThrowInvalidArgument(nil, "COMMAND-00p2b", "Errors.Org.Empty") @@ -522,7 +473,7 @@ func (c *Commands) importHuman(ctx context.Context, orgID string, human *domain. if err := human.Normalize(); err != nil { return nil, nil, nil, "", err } - events, humanWriteModel, err = c.createHuman(ctx, orgID, human, links, false, passwordless, domainPolicy, pwPolicy, initCodeGenerator, emailCodeGenerator, phoneCodeGenerator) + events, humanWriteModel, err = c.createHuman(ctx, orgID, human, links, passwordless, domainPolicy, pwPolicy, initCodeGenerator, emailCodeGenerator, phoneCodeGenerator) if err != nil { return nil, nil, nil, "", err } @@ -537,33 +488,8 @@ func (c *Commands) importHuman(ctx context.Context, orgID string, human *domain. return events, humanWriteModel, passwordlessCodeWriteModel, code, nil } -func (c *Commands) registerHuman(ctx context.Context, orgID string, human *domain.Human, link *domain.UserIDPLink, domainPolicy *domain.DomainPolicy, pwPolicy *domain.PasswordComplexityPolicy, initCodeGenerator, emailCodeGenerator, phoneCodeGenerator crypto.Generator) ([]eventstore.Command, *HumanWriteModel, error) { - if human == nil { - return nil, nil, zerrors.ThrowInvalidArgument(nil, "COMMAND-JKefw", "Errors.User.Invalid") - } - if human.Username = strings.TrimSpace(human.Username); human.Username == "" { - human.Username = string(human.EmailAddress) - } - if orgID == "" { - return nil, nil, zerrors.ThrowInvalidArgument(nil, "COMMAND-hYsVH", "Errors.Org.Empty") - } - if err := human.Normalize(); err != nil { - return nil, nil, err - } - if link == nil && (human.Password == nil || human.Password.SecretString == "") { - return nil, nil, zerrors.ThrowInvalidArgument(nil, "COMMAND-X23na", "Errors.User.Password.Empty") - } - if human.Password != nil && human.Password.SecretString != "" { - human.Password.ChangeRequired = false - } - var links []*domain.UserIDPLink - if link != nil { - links = append(links, link) - } - return c.createHuman(ctx, orgID, human, links, true, false, domainPolicy, pwPolicy, initCodeGenerator, emailCodeGenerator, phoneCodeGenerator) -} - -func (c *Commands) createHuman(ctx context.Context, orgID string, human *domain.Human, links []*domain.UserIDPLink, selfregister, passwordless bool, domainPolicy *domain.DomainPolicy, pwPolicy *domain.PasswordComplexityPolicy, initCodeGenerator, emailCodeGenerator, phoneCodeGenerator crypto.Generator) (events []eventstore.Command, addedHuman *HumanWriteModel, err error) { +//nolint:gocognit +func (c *Commands) createHuman(ctx context.Context, orgID string, human *domain.Human, links []*domain.UserIDPLink, passwordless bool, domainPolicy *domain.DomainPolicy, pwPolicy *domain.PasswordComplexityPolicy, initCodeGenerator, emailCodeGenerator, phoneCodeGenerator crypto.Generator) (events []eventstore.Command, addedHuman *HumanWriteModel, err error) { if err := human.CheckDomainPolicy(domainPolicy); err != nil { return nil, nil, err } @@ -601,11 +527,7 @@ func (c *Commands) createHuman(ctx context.Context, orgID string, human *domain. //TODO: adlerhurst maybe we could simplify the code below userAgg := UserAggregateFromWriteModel(&addedHuman.WriteModel) - if selfregister { - events = append(events, createRegisterHumanEvent(ctx, userAgg, human, domainPolicy.UserLoginMustBeDomain)) - } else { - events = append(events, createAddHumanEvent(ctx, userAgg, human, domainPolicy.UserLoginMustBeDomain)) - } + events = append(events, createAddHumanEvent(ctx, userAgg, human, domainPolicy.UserLoginMustBeDomain)) for _, link := range links { event, err := c.addUserIDPLink(ctx, userAgg, link, false) @@ -620,7 +542,7 @@ func (c *Commands) createHuman(ctx context.Context, orgID string, human *domain. if err != nil { return nil, nil, err } - events = append(events, user.NewHumanInitialCodeAddedEvent(ctx, userAgg, initCode.Code, initCode.Expiry)) + events = append(events, user.NewHumanInitialCodeAddedEvent(ctx, userAgg, initCode.Code, initCode.Expiry, "")) } else { if human.Email != nil && human.EmailAddress != "" && human.IsEmailVerified { events = append(events, user.NewHumanEmailVerifiedEvent(ctx, userAgg)) @@ -629,7 +551,7 @@ func (c *Commands) createHuman(ctx context.Context, orgID string, human *domain. if err != nil { return nil, nil, err } - events = append(events, user.NewHumanEmailCodeAddedEvent(ctx, userAgg, emailCode.Code, emailCode.Expiry)) + events = append(events, user.NewHumanEmailCodeAddedEvent(ctx, userAgg, emailCode.Code, emailCode.Expiry, "")) } } @@ -699,40 +621,6 @@ func createAddHumanEvent(ctx context.Context, aggregate *eventstore.Aggregate, h return addEvent } -func createRegisterHumanEvent(ctx context.Context, aggregate *eventstore.Aggregate, human *domain.Human, userLoginMustBeDomain bool) *user.HumanRegisteredEvent { - addEvent := user.NewHumanRegisteredEvent( - ctx, - aggregate, - human.Username, - human.FirstName, - human.LastName, - human.NickName, - human.DisplayName, - human.PreferredLanguage, - human.Gender, - human.EmailAddress, - userLoginMustBeDomain, - ) - if human.Phone != nil { - addEvent.AddPhoneData(human.PhoneNumber) - } - if human.Address != nil { - addEvent.AddAddressData( - human.Country, - human.Locality, - human.PostalCode, - human.Region, - human.StreetAddress) - } - if human.Password != nil { - addEvent.AddPasswordData(human.Password.EncodedSecret, human.Password.ChangeRequired) - } - if human.HashedPassword != "" { - addEvent.AddPasswordData(human.HashedPassword, false) - } - return addEvent -} - func (c *Commands) HumansSignOut(ctx context.Context, agentID string, userIDs []string) error { if agentID == "" { return zerrors.ThrowInvalidArgument(nil, "COMMAND-2M0ds", "Errors.User.UserIDMissing") @@ -783,3 +671,53 @@ func humanWriteModelByID(ctx context.Context, filter preparation.FilterToQueryRe err = humanWriteModel.Reduce() return humanWriteModel, err } + +func AddHumanFromDomain(user *domain.Human, metadataList []*domain.Metadata, authRequest *domain.AuthRequest, idp *domain.UserIDPLink) *AddHuman { + addMetadata := make([]*AddMetadataEntry, len(metadataList)) + for i, metadata := range metadataList { + addMetadata[i] = &AddMetadataEntry{ + Key: metadata.Key, + Value: metadata.Value, + } + } + human := new(AddHuman) + if user.Profile != nil { + human.Username = user.Username + human.FirstName = user.FirstName + human.LastName = user.LastName + human.NickName = user.NickName + human.DisplayName = user.DisplayName + human.PreferredLanguage = user.PreferredLanguage + human.Gender = user.Gender + human.Password = user.Password.SecretString + human.Register = true + human.Metadata = addMetadata + human.UserAgentID = authRequest.AgentID + human.AuthRequestID = authRequest.ID + } + if user.Email != nil { + human.Email = Email{ + Address: user.EmailAddress, + Verified: user.IsEmailVerified, + } + } + if user.Phone != nil { + human.Phone = Phone{ + Number: user.Phone.PhoneNumber, + Verified: user.Phone.IsPhoneVerified, + } + } + if idp != nil { + human.Links = []*AddLink{ + { + IDPID: idp.IDPConfigID, + DisplayName: idp.DisplayName, + IDPExternalID: idp.ExternalUserID, + }, + } + } + if human.Username = strings.TrimSpace(human.Username); human.Username == "" { + human.Username = string(human.Email.Address) + } + return human +} diff --git a/internal/command/user_human_email.go b/internal/command/user_human_email.go index 4607b1294..18bdc1caa 100644 --- a/internal/command/user_human_email.go +++ b/internal/command/user_human_email.go @@ -50,7 +50,7 @@ func (c *Commands) ChangeHumanEmail(ctx context.Context, email *domain.Email, em if err != nil { return nil, err } - events = append(events, user.NewHumanEmailCodeAddedEvent(ctx, userAgg, emailCode.Code, emailCode.Expiry)) + events = append(events, user.NewHumanEmailCodeAddedEvent(ctx, userAgg, emailCode.Code, emailCode.Expiry, "")) } pushedEvents, err := c.eventstore.Push(ctx, events...) @@ -99,7 +99,7 @@ func (c *Commands) VerifyHumanEmail(ctx context.Context, userID, code, resourceo return nil, zerrors.ThrowInvalidArgument(err, "COMMAND-Gdsgs", "Errors.User.Code.Invalid") } -func (c *Commands) CreateHumanEmailVerificationCode(ctx context.Context, userID, resourceOwner string, emailCodeGenerator crypto.Generator) (*domain.ObjectDetails, error) { +func (c *Commands) CreateHumanEmailVerificationCode(ctx context.Context, userID, resourceOwner string, emailCodeGenerator crypto.Generator, authRequestID string) (*domain.ObjectDetails, error) { if userID == "" { return nil, zerrors.ThrowInvalidArgument(nil, "COMMAND-4M0ds", "Errors.User.UserIDMissing") } @@ -122,7 +122,10 @@ func (c *Commands) CreateHumanEmailVerificationCode(ctx context.Context, userID, if err != nil { return nil, err } - pushedEvents, err := c.eventstore.Push(ctx, user.NewHumanEmailCodeAddedEvent(ctx, userAgg, emailCode.Code, emailCode.Expiry)) + if authRequestID == "" { + authRequestID = existingEmail.AuthRequestID + } + pushedEvents, err := c.eventstore.Push(ctx, user.NewHumanEmailCodeAddedEvent(ctx, userAgg, emailCode.Code, emailCode.Expiry, authRequestID)) if err != nil { return nil, err } diff --git a/internal/command/user_human_email_model.go b/internal/command/user_human_email_model.go index d05881a79..b60eef5d2 100644 --- a/internal/command/user_human_email_model.go +++ b/internal/command/user_human_email_model.go @@ -19,6 +19,7 @@ type HumanEmailWriteModel struct { Code *crypto.CryptoValue CodeCreationDate time.Time CodeExpiry time.Duration + AuthRequestID string UserState domain.UserState } @@ -53,6 +54,7 @@ func (wm *HumanEmailWriteModel) Reduce() error { wm.Code = e.Code wm.CodeCreationDate = e.CreationDate() wm.CodeExpiry = e.Expiry + wm.AuthRequestID = e.AuthRequestID case *user.HumanEmailVerifiedEvent: wm.IsEmailVerified = true wm.Code = nil diff --git a/internal/command/user_human_email_test.go b/internal/command/user_human_email_test.go index 7abd65efe..53b95ac80 100644 --- a/internal/command/user_human_email_test.go +++ b/internal/command/user_human_email_test.go @@ -18,7 +18,7 @@ import ( func TestCommandSide_ChangeHumanEmail(t *testing.T) { type fields struct { - eventstore *eventstore.Eventstore + eventstore func(*testing.T) *eventstore.Eventstore } type args struct { ctx context.Context @@ -39,9 +39,7 @@ func TestCommandSide_ChangeHumanEmail(t *testing.T) { { name: "invalid email, invalid argument error", fields: fields{ - eventstore: eventstoreExpect( - t, - ), + eventstore: expectEventstore(), }, args: args{ ctx: context.Background(), @@ -59,8 +57,7 @@ func TestCommandSide_ChangeHumanEmail(t *testing.T) { { name: "user not existing, precondition error", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter(), ), }, @@ -81,8 +78,7 @@ func TestCommandSide_ChangeHumanEmail(t *testing.T) { { name: "user not initialized, precondition error", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter( eventFromEventPusher( user.NewHumanAddedEvent(context.Background(), @@ -102,6 +98,7 @@ func TestCommandSide_ChangeHumanEmail(t *testing.T) { user.NewHumanInitialCodeAddedEvent(context.Background(), &user.NewAggregate("user1", "org1").Aggregate, nil, time.Hour*1, + "", ), ), ), @@ -124,8 +121,7 @@ func TestCommandSide_ChangeHumanEmail(t *testing.T) { { name: "email not changed, precondition error", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter( eventFromEventPusher( user.NewHumanAddedEvent(context.Background(), @@ -161,8 +157,7 @@ func TestCommandSide_ChangeHumanEmail(t *testing.T) { { name: "verified email changed, ok", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter( eventFromEventPusher( user.NewHumanAddedEvent(context.Background(), @@ -215,8 +210,7 @@ func TestCommandSide_ChangeHumanEmail(t *testing.T) { { name: "email verified, ok", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter( eventFromEventPusher( user.NewHumanAddedEvent(context.Background(), @@ -265,8 +259,7 @@ func TestCommandSide_ChangeHumanEmail(t *testing.T) { { name: "email verified, ok", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter( eventFromEventPusher( user.NewHumanAddedEvent(context.Background(), @@ -315,8 +308,7 @@ func TestCommandSide_ChangeHumanEmail(t *testing.T) { { name: "email changed with code, ok", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter( eventFromEventPusher( user.NewHumanAddedEvent(context.Background(), @@ -347,6 +339,7 @@ func TestCommandSide_ChangeHumanEmail(t *testing.T) { Crypted: []byte("a"), }, time.Hour*1, + "", ), ), ), @@ -376,7 +369,7 @@ func TestCommandSide_ChangeHumanEmail(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { r := &Commands{ - eventstore: tt.fields.eventstore, + eventstore: tt.fields.eventstore(t), } got, err := r.ChangeHumanEmail(tt.args.ctx, tt.args.email, tt.args.secretGenerator) if tt.res.err == nil { @@ -394,7 +387,7 @@ func TestCommandSide_ChangeHumanEmail(t *testing.T) { func TestCommandSide_VerifyHumanEmail(t *testing.T) { type fields struct { - eventstore *eventstore.Eventstore + eventstore func(*testing.T) *eventstore.Eventstore } type args struct { ctx context.Context @@ -416,9 +409,7 @@ func TestCommandSide_VerifyHumanEmail(t *testing.T) { { name: "userid missing, invalid argument error", fields: fields{ - eventstore: eventstoreExpect( - t, - ), + eventstore: expectEventstore(), }, args: args{ ctx: context.Background(), @@ -432,9 +423,7 @@ func TestCommandSide_VerifyHumanEmail(t *testing.T) { { name: "code missing, invalid argument error", fields: fields{ - eventstore: eventstoreExpect( - t, - ), + eventstore: expectEventstore(), }, args: args{ ctx: context.Background(), @@ -448,8 +437,7 @@ func TestCommandSide_VerifyHumanEmail(t *testing.T) { { name: "user not existing, precondition error", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter(), ), }, @@ -466,8 +454,7 @@ func TestCommandSide_VerifyHumanEmail(t *testing.T) { { name: "code not existing, precondition error", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter( eventFromEventPusher( user.NewHumanAddedEvent(context.Background(), @@ -499,8 +486,7 @@ func TestCommandSide_VerifyHumanEmail(t *testing.T) { { name: "invalid code, invalid argument error", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter( eventFromEventPusher( user.NewHumanAddedEvent(context.Background(), @@ -526,6 +512,7 @@ func TestCommandSide_VerifyHumanEmail(t *testing.T) { Crypted: []byte("a"), }, time.Hour*1, + "", ), ), ), @@ -550,8 +537,7 @@ func TestCommandSide_VerifyHumanEmail(t *testing.T) { { name: "valid code, ok", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter( eventFromEventPusher( user.NewHumanAddedEvent(context.Background(), @@ -577,6 +563,7 @@ func TestCommandSide_VerifyHumanEmail(t *testing.T) { Crypted: []byte("a"), }, time.Hour*1, + "", ), ), ), @@ -604,7 +591,7 @@ func TestCommandSide_VerifyHumanEmail(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { r := &Commands{ - eventstore: tt.fields.eventstore, + eventstore: tt.fields.eventstore(t), } got, err := r.VerifyHumanEmail(tt.args.ctx, tt.args.userID, tt.args.code, tt.args.resourceOwner, tt.args.secretGenerator) if tt.res.err == nil { @@ -622,13 +609,14 @@ func TestCommandSide_VerifyHumanEmail(t *testing.T) { func TestCommandSide_CreateVerificationCodeHumanEmail(t *testing.T) { type fields struct { - eventstore *eventstore.Eventstore + eventstore func(*testing.T) *eventstore.Eventstore } type args struct { ctx context.Context userID string resourceOwner string secretGenerator crypto.Generator + authRequestID string } type res struct { want *domain.ObjectDetails @@ -643,9 +631,7 @@ func TestCommandSide_CreateVerificationCodeHumanEmail(t *testing.T) { { name: "userid missing, invalid argument error", fields: fields{ - eventstore: eventstoreExpect( - t, - ), + eventstore: expectEventstore(), }, args: args{ ctx: context.Background(), @@ -658,8 +644,7 @@ func TestCommandSide_CreateVerificationCodeHumanEmail(t *testing.T) { { name: "user not existing, precondition error", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter(), ), }, @@ -675,8 +660,7 @@ func TestCommandSide_CreateVerificationCodeHumanEmail(t *testing.T) { { name: "user not initialized, precondition error", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter( eventFromEventPusher( user.NewHumanAddedEvent(context.Background(), @@ -696,6 +680,7 @@ func TestCommandSide_CreateVerificationCodeHumanEmail(t *testing.T) { user.NewHumanInitialCodeAddedEvent(context.Background(), &user.NewAggregate("user1", "org1").Aggregate, nil, time.Hour*1, + "", ), ), ), @@ -713,8 +698,7 @@ func TestCommandSide_CreateVerificationCodeHumanEmail(t *testing.T) { { name: "email already verified, precondition error", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter( eventFromEventPusher( user.NewHumanAddedEvent(context.Background(), @@ -750,8 +734,7 @@ func TestCommandSide_CreateVerificationCodeHumanEmail(t *testing.T) { { name: "new code, ok", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter( eventFromEventPusher( user.NewHumanAddedEvent(context.Background(), @@ -789,6 +772,7 @@ func TestCommandSide_CreateVerificationCodeHumanEmail(t *testing.T) { Crypted: []byte("a"), }, time.Hour*1, + "", ), ), ), @@ -805,13 +789,72 @@ func TestCommandSide_CreateVerificationCodeHumanEmail(t *testing.T) { }, }, }, + { + name: "new code with authRequestID, ok", + fields: fields{ + eventstore: expectEventstore( + expectFilter( + eventFromEventPusher( + user.NewHumanAddedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "username", + "firstname", + "lastname", + "nickname", + "displayname", + language.German, + domain.GenderUnspecified, + "email@test.ch", + true, + ), + ), + eventFromEventPusher( + user.NewHumanEmailVerifiedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + ), + ), + eventFromEventPusher( + user.NewHumanEmailChangedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "email2@test.ch", + ), + ), + ), + expectPush( + user.NewHumanEmailCodeAddedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("a"), + }, + time.Hour*1, + "authRequestID", + ), + ), + ), + }, + args: args{ + ctx: context.Background(), + userID: "user1", + resourceOwner: "org1", + secretGenerator: GetMockSecretGenerator(t), + authRequestID: "authRequestID", + }, + res: res{ + want: &domain.ObjectDetails{ + ResourceOwner: "org1", + }, + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { r := &Commands{ - eventstore: tt.fields.eventstore, + eventstore: tt.fields.eventstore(t), } - got, err := r.CreateHumanEmailVerificationCode(tt.args.ctx, tt.args.userID, tt.args.resourceOwner, tt.args.secretGenerator) + got, err := r.CreateHumanEmailVerificationCode(tt.args.ctx, tt.args.userID, tt.args.resourceOwner, tt.args.secretGenerator, tt.args.authRequestID) if tt.res.err == nil { assert.NoError(t, err) } @@ -827,7 +870,7 @@ func TestCommandSide_CreateVerificationCodeHumanEmail(t *testing.T) { func TestCommandSide_EmailVerificationCodeSent(t *testing.T) { type fields struct { - eventstore *eventstore.Eventstore + eventstore func(*testing.T) *eventstore.Eventstore } type args struct { ctx context.Context @@ -846,9 +889,7 @@ func TestCommandSide_EmailVerificationCodeSent(t *testing.T) { { name: "userid missing, invalid argument error", fields: fields{ - eventstore: eventstoreExpect( - t, - ), + eventstore: expectEventstore(), }, args: args{ ctx: context.Background(), @@ -861,8 +902,7 @@ func TestCommandSide_EmailVerificationCodeSent(t *testing.T) { { name: "user not existing, precondition error", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter(), ), }, @@ -878,8 +918,7 @@ func TestCommandSide_EmailVerificationCodeSent(t *testing.T) { { name: "code sent, ok", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter( eventFromEventPusher( user.NewHumanAddedEvent(context.Background(), @@ -925,7 +964,7 @@ func TestCommandSide_EmailVerificationCodeSent(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { r := &Commands{ - eventstore: tt.fields.eventstore, + eventstore: tt.fields.eventstore(t), } err := r.HumanEmailVerificationCodeSent(tt.args.ctx, tt.args.resourceOwner, tt.args.userID) if tt.res.err == nil { diff --git a/internal/command/user_human_init.go b/internal/command/user_human_init.go index f3ec27a19..60160743c 100644 --- a/internal/command/user_human_init.go +++ b/internal/command/user_human_init.go @@ -13,7 +13,7 @@ import ( ) // ResendInitialMail resend initial mail and changes email if provided -func (c *Commands) ResendInitialMail(ctx context.Context, userID string, email domain.EmailAddress, resourceOwner string, initCodeGenerator crypto.Generator) (objectDetails *domain.ObjectDetails, err error) { +func (c *Commands) ResendInitialMail(ctx context.Context, userID string, email domain.EmailAddress, resourceOwner string, initCodeGenerator crypto.Generator, authRequestID string) (objectDetails *domain.ObjectDetails, err error) { if userID == "" { return nil, zerrors.ThrowInvalidArgument(nil, "COMMAND-2n8vs", "Errors.User.UserIDMissing") } @@ -38,7 +38,10 @@ func (c *Commands) ResendInitialMail(ctx context.Context, userID string, email d if err != nil { return nil, err } - events = append(events, user.NewHumanInitialCodeAddedEvent(ctx, userAgg, initCode.Code, initCode.Expiry)) + if authRequestID == "" { + authRequestID = existingCode.AuthRequestID + } + events = append(events, user.NewHumanInitialCodeAddedEvent(ctx, userAgg, initCode.Code, initCode.Expiry, authRequestID)) pushedEvents, err := c.eventstore.Push(ctx, events...) if err != nil { return nil, err diff --git a/internal/command/user_human_init_model.go b/internal/command/user_human_init_model.go index c1276ec7c..37c60a4b0 100644 --- a/internal/command/user_human_init_model.go +++ b/internal/command/user_human_init_model.go @@ -19,6 +19,7 @@ type HumanInitCodeWriteModel struct { Code *crypto.CryptoValue CodeCreationDate time.Time CodeExpiry time.Duration + AuthRequestID string UserState domain.UserState } @@ -50,6 +51,7 @@ func (wm *HumanInitCodeWriteModel) Reduce() error { wm.Code = e.Code wm.CodeCreationDate = e.CreationDate() wm.CodeExpiry = e.Expiry + wm.AuthRequestID = e.AuthRequestID wm.UserState = domain.UserStateInitial case *user.HumanInitializedCheckSucceededEvent: wm.Code = nil diff --git a/internal/command/user_human_init_test.go b/internal/command/user_human_init_test.go index d89671d76..382d2892b 100644 --- a/internal/command/user_human_init_test.go +++ b/internal/command/user_human_init_test.go @@ -18,7 +18,7 @@ import ( func TestCommandSide_ResendInitialMail(t *testing.T) { type fields struct { - eventstore *eventstore.Eventstore + eventstore func(*testing.T) *eventstore.Eventstore } type args struct { ctx context.Context @@ -26,6 +26,7 @@ func TestCommandSide_ResendInitialMail(t *testing.T) { email string resourceOwner string secretGenerator crypto.Generator + authRequestID string } type res struct { want *domain.ObjectDetails @@ -40,9 +41,7 @@ func TestCommandSide_ResendInitialMail(t *testing.T) { { name: "userid missing, invalid argument error", fields: fields{ - eventstore: eventstoreExpect( - t, - ), + eventstore: expectEventstore(), }, args: args{ ctx: context.Background(), @@ -55,8 +54,7 @@ func TestCommandSide_ResendInitialMail(t *testing.T) { { name: "user not existing, precondition error", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter(), ), }, @@ -72,8 +70,7 @@ func TestCommandSide_ResendInitialMail(t *testing.T) { { name: "user not initialized, precondition error", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter( eventFromEventPusher( user.NewHumanAddedEvent(context.Background(), @@ -107,8 +104,7 @@ func TestCommandSide_ResendInitialMail(t *testing.T) { { name: "new code email not changed, ok", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter( eventFromEventPusher( user.NewHumanAddedEvent(context.Background(), @@ -128,6 +124,7 @@ func TestCommandSide_ResendInitialMail(t *testing.T) { user.NewHumanInitialCodeAddedEvent(context.Background(), &user.NewAggregate("user1", "org1").Aggregate, nil, time.Hour*1, + "", ), ), ), @@ -141,6 +138,7 @@ func TestCommandSide_ResendInitialMail(t *testing.T) { Crypted: []byte("a"), }, time.Hour*1, + "", ), ), ), @@ -159,10 +157,9 @@ func TestCommandSide_ResendInitialMail(t *testing.T) { }, }, { - name: "new code, ok", + name: "new code email not changed with authRequestID, ok", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter( eventFromEventPusher( user.NewHumanAddedEvent(context.Background(), @@ -182,6 +179,7 @@ func TestCommandSide_ResendInitialMail(t *testing.T) { user.NewHumanInitialCodeAddedEvent(context.Background(), &user.NewAggregate("user1", "org1").Aggregate, nil, time.Hour*1, + "authRequestID", ), ), ), @@ -195,6 +193,63 @@ func TestCommandSide_ResendInitialMail(t *testing.T) { Crypted: []byte("a"), }, time.Hour*1, + "authRequestID", + ), + ), + ), + }, + args: args{ + ctx: context.Background(), + userID: "user1", + resourceOwner: "org1", + email: "email@test.ch", + secretGenerator: GetMockSecretGenerator(t), + authRequestID: "authRequestID", + }, + res: res{ + want: &domain.ObjectDetails{ + ResourceOwner: "org1", + }, + }, + }, + { + name: "new code, ok", + fields: fields{ + eventstore: expectEventstore( + expectFilter( + eventFromEventPusher( + user.NewHumanAddedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "username", + "firstname", + "lastname", + "nickname", + "displayname", + language.German, + domain.GenderUnspecified, + "email@test.ch", + true, + ), + ), + eventFromEventPusher( + user.NewHumanInitialCodeAddedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + nil, time.Hour*1, + "", + ), + ), + ), + expectPush( + user.NewHumanInitialCodeAddedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("a"), + }, + time.Hour*1, + "", ), ), ), @@ -212,10 +267,9 @@ func TestCommandSide_ResendInitialMail(t *testing.T) { }, }, { - name: "new code with change email, ok", + name: "new code with authRequestID, ok", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter( eventFromEventPusher( user.NewHumanAddedEvent(context.Background(), @@ -235,6 +289,62 @@ func TestCommandSide_ResendInitialMail(t *testing.T) { user.NewHumanInitialCodeAddedEvent(context.Background(), &user.NewAggregate("user1", "org1").Aggregate, nil, time.Hour*1, + "authRequestID", + ), + ), + ), + expectPush( + user.NewHumanInitialCodeAddedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("a"), + }, + time.Hour*1, + "authRequestID", + ), + ), + ), + }, + args: args{ + ctx: context.Background(), + userID: "user1", + resourceOwner: "org1", + secretGenerator: GetMockSecretGenerator(t), + authRequestID: "authRequestID", + }, + res: res{ + want: &domain.ObjectDetails{ + ResourceOwner: "org1", + }, + }, + }, + { + name: "new code with change email, ok", + fields: fields{ + eventstore: expectEventstore( + expectFilter( + eventFromEventPusher( + user.NewHumanAddedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "username", + "firstname", + "lastname", + "nickname", + "displayname", + language.German, + domain.GenderUnspecified, + "email@test.ch", + true, + ), + ), + eventFromEventPusher( + user.NewHumanInitialCodeAddedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + nil, time.Hour*1, + "", ), ), ), @@ -252,6 +362,7 @@ func TestCommandSide_ResendInitialMail(t *testing.T) { Crypted: []byte("a"), }, time.Hour*1, + "", ), ), ), @@ -273,9 +384,9 @@ func TestCommandSide_ResendInitialMail(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { r := &Commands{ - eventstore: tt.fields.eventstore, + eventstore: tt.fields.eventstore(t), } - got, err := r.ResendInitialMail(tt.args.ctx, tt.args.userID, domain.EmailAddress(tt.args.email), tt.args.resourceOwner, tt.args.secretGenerator) + got, err := r.ResendInitialMail(tt.args.ctx, tt.args.userID, domain.EmailAddress(tt.args.email), tt.args.resourceOwner, tt.args.secretGenerator, tt.args.authRequestID) if tt.res.err == nil { assert.NoError(t, err) } @@ -291,7 +402,7 @@ func TestCommandSide_ResendInitialMail(t *testing.T) { func TestCommandSide_VerifyInitCode(t *testing.T) { type fields struct { - eventstore *eventstore.Eventstore + eventstore func(*testing.T) *eventstore.Eventstore userPasswordHasher *crypto.Hasher } type args struct { @@ -316,9 +427,7 @@ func TestCommandSide_VerifyInitCode(t *testing.T) { { name: "userid missing, invalid argument error", fields: fields{ - eventstore: eventstoreExpect( - t, - ), + eventstore: expectEventstore(), }, args: args{ ctx: context.Background(), @@ -332,9 +441,7 @@ func TestCommandSide_VerifyInitCode(t *testing.T) { { name: "code missing, invalid argument error", fields: fields{ - eventstore: eventstoreExpect( - t, - ), + eventstore: expectEventstore(), }, args: args{ ctx: context.Background(), @@ -348,8 +455,7 @@ func TestCommandSide_VerifyInitCode(t *testing.T) { { name: "user not existing, precondition error", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter(), ), }, @@ -366,8 +472,7 @@ func TestCommandSide_VerifyInitCode(t *testing.T) { { name: "code not existing, precondition error", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter( eventFromEventPusher( user.NewHumanAddedEvent(context.Background(), @@ -399,8 +504,7 @@ func TestCommandSide_VerifyInitCode(t *testing.T) { { name: "invalid code, invalid argument error", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter( eventFromEventPusher( user.NewHumanAddedEvent(context.Background(), @@ -426,6 +530,7 @@ func TestCommandSide_VerifyInitCode(t *testing.T) { Crypted: []byte("a"), }, time.Hour*1, + "", ), ), ), @@ -450,8 +555,7 @@ func TestCommandSide_VerifyInitCode(t *testing.T) { { name: "valid code, ok", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter( eventFromEventPusher( user.NewHumanAddedEvent(context.Background(), @@ -477,6 +581,7 @@ func TestCommandSide_VerifyInitCode(t *testing.T) { Crypted: []byte("a"), }, time.Hour*1, + "", ), ), ), @@ -506,8 +611,7 @@ func TestCommandSide_VerifyInitCode(t *testing.T) { { name: "valid code with password, ok", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter( eventFromEventPusher( user.NewHumanAddedEvent(context.Background(), @@ -536,6 +640,7 @@ func TestCommandSide_VerifyInitCode(t *testing.T) { Crypted: []byte("a"), }, time.Hour*1, + "", ), ), ), @@ -582,8 +687,7 @@ func TestCommandSide_VerifyInitCode(t *testing.T) { { name: "valid code with password and userAgentID, ok", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter( eventFromEventPusher( user.NewHumanAddedEvent(context.Background(), @@ -612,6 +716,7 @@ func TestCommandSide_VerifyInitCode(t *testing.T) { Crypted: []byte("a"), }, time.Hour*1, + "", ), ), ), @@ -660,7 +765,7 @@ func TestCommandSide_VerifyInitCode(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { r := &Commands{ - eventstore: tt.fields.eventstore, + eventstore: tt.fields.eventstore(t), userPasswordHasher: tt.fields.userPasswordHasher, } err := r.HumanVerifyInitCode(tt.args.ctx, tt.args.userID, tt.args.resourceOwner, tt.args.code, tt.args.password, tt.args.userAgentID, tt.args.secretGenerator) @@ -676,7 +781,7 @@ func TestCommandSide_VerifyInitCode(t *testing.T) { func TestCommandSide_InitCodeSent(t *testing.T) { type fields struct { - eventstore *eventstore.Eventstore + eventstore func(*testing.T) *eventstore.Eventstore } type args struct { ctx context.Context @@ -695,9 +800,7 @@ func TestCommandSide_InitCodeSent(t *testing.T) { { name: "userid missing, invalid argument error", fields: fields{ - eventstore: eventstoreExpect( - t, - ), + eventstore: expectEventstore(), }, args: args{ ctx: context.Background(), @@ -710,8 +813,7 @@ func TestCommandSide_InitCodeSent(t *testing.T) { { name: "user not existing, precondition error", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter(), ), }, @@ -727,8 +829,7 @@ func TestCommandSide_InitCodeSent(t *testing.T) { { name: "code sent, ok", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter( eventFromEventPusher( user.NewHumanAddedEvent(context.Background(), @@ -763,7 +864,7 @@ func TestCommandSide_InitCodeSent(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { r := &Commands{ - eventstore: tt.fields.eventstore, + eventstore: tt.fields.eventstore(t), } err := r.HumanInitCodeSent(tt.args.ctx, tt.args.resourceOwner, tt.args.userID) if tt.res.err == nil { diff --git a/internal/command/user_human_password.go b/internal/command/user_human_password.go index 8831bff90..d7567c647 100644 --- a/internal/command/user_human_password.go +++ b/internal/command/user_human_password.go @@ -165,7 +165,7 @@ func (c *Commands) canUpdatePassword(ctx context.Context, newPassword string, re } // RequestSetPassword generate and send out new code to change password for a specific user -func (c *Commands) RequestSetPassword(ctx context.Context, userID, resourceOwner string, notifyType domain.NotificationType, passwordVerificationCode crypto.Generator) (objectDetails *domain.ObjectDetails, err error) { +func (c *Commands) RequestSetPassword(ctx context.Context, userID, resourceOwner string, notifyType domain.NotificationType, passwordVerificationCode crypto.Generator, authRequestID string) (objectDetails *domain.ObjectDetails, err error) { if userID == "" { return nil, zerrors.ThrowInvalidArgument(nil, "COMMAND-M00oL", "Errors.User.UserIDMissing") } @@ -185,7 +185,7 @@ func (c *Commands) RequestSetPassword(ctx context.Context, userID, resourceOwner if err != nil { return nil, err } - pushedEvents, err := c.eventstore.Push(ctx, user.NewHumanPasswordCodeAddedEvent(ctx, userAgg, passwordCode.Code, passwordCode.Expiry, notifyType)) + pushedEvents, err := c.eventstore.Push(ctx, user.NewHumanPasswordCodeAddedEvent(ctx, userAgg, passwordCode.Code, passwordCode.Expiry, notifyType, authRequestID)) if err != nil { return nil, err } diff --git a/internal/command/user_human_password_test.go b/internal/command/user_human_password_test.go index 0c2b21b3d..965309ef5 100644 --- a/internal/command/user_human_password_test.go +++ b/internal/command/user_human_password_test.go @@ -22,7 +22,7 @@ import ( func TestCommandSide_SetOneTimePassword(t *testing.T) { type fields struct { - eventstore *eventstore.Eventstore + eventstore func(*testing.T) *eventstore.Eventstore userPasswordHasher *crypto.Hasher checkPermission domain.PermissionCheck } @@ -46,9 +46,7 @@ func TestCommandSide_SetOneTimePassword(t *testing.T) { { name: "userid missing, invalid argument error", fields: fields{ - eventstore: eventstoreExpect( - t, - ), + eventstore: expectEventstore(), }, args: args{ ctx: context.Background(), @@ -61,8 +59,7 @@ func TestCommandSide_SetOneTimePassword(t *testing.T) { { name: "user not existing, precondition error", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter(), ), }, @@ -78,8 +75,7 @@ func TestCommandSide_SetOneTimePassword(t *testing.T) { { name: "missing permission, error", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter( eventFromEventPusher( user.NewHumanAddedEvent(context.Background(), @@ -121,8 +117,7 @@ func TestCommandSide_SetOneTimePassword(t *testing.T) { { name: "change password onetime, ok", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter( eventFromEventPusher( user.NewHumanAddedEvent(context.Background(), @@ -184,8 +179,7 @@ func TestCommandSide_SetOneTimePassword(t *testing.T) { { name: "change password no one time, ok", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter( eventFromEventPusher( user.NewHumanAddedEvent(context.Background(), @@ -248,7 +242,7 @@ func TestCommandSide_SetOneTimePassword(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { r := &Commands{ - eventstore: tt.fields.eventstore, + eventstore: tt.fields.eventstore(t), userPasswordHasher: tt.fields.userPasswordHasher, checkPermission: tt.fields.checkPermission, } @@ -268,7 +262,7 @@ func TestCommandSide_SetOneTimePassword(t *testing.T) { func TestCommandSide_SetPasswordWithVerifyCode(t *testing.T) { type fields struct { - eventstore *eventstore.Eventstore + eventstore func(*testing.T) *eventstore.Eventstore userEncryption crypto.EncryptionAlgorithm userPasswordHasher *crypto.Hasher } @@ -293,9 +287,7 @@ func TestCommandSide_SetPasswordWithVerifyCode(t *testing.T) { { name: "userid missing, invalid argument error", fields: fields{ - eventstore: eventstoreExpect( - t, - ), + eventstore: expectEventstore(), }, args: args{ ctx: context.Background(), @@ -308,9 +300,7 @@ func TestCommandSide_SetPasswordWithVerifyCode(t *testing.T) { { name: "password missing, invalid argument error", fields: fields{ - eventstore: eventstoreExpect( - t, - ), + eventstore: expectEventstore(), }, args: args{ ctx: context.Background(), @@ -324,8 +314,7 @@ func TestCommandSide_SetPasswordWithVerifyCode(t *testing.T) { { name: "user not existing, precondition error", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter(), ), }, @@ -342,8 +331,7 @@ func TestCommandSide_SetPasswordWithVerifyCode(t *testing.T) { { name: "code not existing, precondition error", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter( eventFromEventPusher( user.NewHumanAddedEvent(context.Background(), @@ -376,8 +364,7 @@ func TestCommandSide_SetPasswordWithVerifyCode(t *testing.T) { { name: "invalid code, invalid argument error", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter( eventFromEventPusher( user.NewHumanAddedEvent(context.Background(), @@ -404,6 +391,7 @@ func TestCommandSide_SetPasswordWithVerifyCode(t *testing.T) { }, time.Hour*1, domain.NotificationTypeEmail, + "", ), ), ), @@ -424,8 +412,7 @@ func TestCommandSide_SetPasswordWithVerifyCode(t *testing.T) { { name: "set password, ok", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter( eventFromEventPusher( user.NewHumanAddedEvent(context.Background(), @@ -457,6 +444,7 @@ func TestCommandSide_SetPasswordWithVerifyCode(t *testing.T) { }, time.Hour*1, domain.NotificationTypeEmail, + "", ), ), ), @@ -500,8 +488,7 @@ func TestCommandSide_SetPasswordWithVerifyCode(t *testing.T) { { name: "set password with userAgentID, ok", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter( eventFromEventPusher( user.NewHumanAddedEvent(context.Background(), @@ -533,6 +520,7 @@ func TestCommandSide_SetPasswordWithVerifyCode(t *testing.T) { }, time.Hour*1, domain.NotificationTypeEmail, + "", ), ), ), @@ -578,7 +566,7 @@ func TestCommandSide_SetPasswordWithVerifyCode(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { r := &Commands{ - eventstore: tt.fields.eventstore, + eventstore: tt.fields.eventstore(t), userPasswordHasher: tt.fields.userPasswordHasher, userEncryption: tt.fields.userEncryption, } @@ -915,7 +903,7 @@ func TestCommandSide_ChangePassword(t *testing.T) { func TestCommandSide_RequestSetPassword(t *testing.T) { type fields struct { - eventstore *eventstore.Eventstore + eventstore func(*testing.T) *eventstore.Eventstore } type args struct { ctx context.Context @@ -923,6 +911,7 @@ func TestCommandSide_RequestSetPassword(t *testing.T) { resourceOwner string notifyType domain.NotificationType secretGenerator crypto.Generator + authRequestID string } type res struct { want *domain.ObjectDetails @@ -937,9 +926,7 @@ func TestCommandSide_RequestSetPassword(t *testing.T) { { name: "userid missing, invalid argument error", fields: fields{ - eventstore: eventstoreExpect( - t, - ), + eventstore: expectEventstore(), }, args: args{ ctx: context.Background(), @@ -952,8 +939,7 @@ func TestCommandSide_RequestSetPassword(t *testing.T) { { name: "user not existing, precondition error", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter(), ), }, @@ -969,8 +955,7 @@ func TestCommandSide_RequestSetPassword(t *testing.T) { { name: "user initial, precondition error", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter( eventFromEventPusher( user.NewHumanAddedEvent(context.Background(), @@ -990,6 +975,7 @@ func TestCommandSide_RequestSetPassword(t *testing.T) { user.NewHumanInitialCodeAddedEvent(context.Background(), &user.NewAggregate("user1", "org1").Aggregate, nil, time.Hour*1, + "", ), ), eventFromEventPusher( @@ -1018,8 +1004,7 @@ func TestCommandSide_RequestSetPassword(t *testing.T) { { name: "new code, ok", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter( eventFromEventPusher( user.NewHumanAddedEvent(context.Background(), @@ -1055,6 +1040,7 @@ func TestCommandSide_RequestSetPassword(t *testing.T) { }, time.Hour*1, domain.NotificationTypeEmail, + "", ), ), ), @@ -1071,13 +1057,70 @@ func TestCommandSide_RequestSetPassword(t *testing.T) { }, }, }, + { + name: "new code with authRequestID, ok", + fields: fields{ + eventstore: expectEventstore( + expectFilter( + eventFromEventPusher( + user.NewHumanAddedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + "username", + "firstname", + "lastname", + "nickname", + "displayname", + language.German, + domain.GenderUnspecified, + "email@test.ch", + true, + ), + ), + eventFromEventPusher( + user.NewHumanEmailVerifiedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + ), + ), + eventFromEventPusher( + user.NewHumanInitializedCheckSucceededEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate)), + ), + expectPush( + user.NewHumanPasswordCodeAddedEvent(context.Background(), + &user.NewAggregate("user1", "org1").Aggregate, + &crypto.CryptoValue{ + CryptoType: crypto.TypeEncryption, + Algorithm: "enc", + KeyID: "id", + Crypted: []byte("a"), + }, + time.Hour*1, + domain.NotificationTypeEmail, + "authRequestID", + ), + ), + ), + }, + args: args{ + ctx: context.Background(), + userID: "user1", + resourceOwner: "org1", + secretGenerator: GetMockSecretGenerator(t), + authRequestID: "authRequestID", + }, + res: res{ + want: &domain.ObjectDetails{ + ResourceOwner: "org1", + }, + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { r := &Commands{ - eventstore: tt.fields.eventstore, + eventstore: tt.fields.eventstore(t), } - got, err := r.RequestSetPassword(tt.args.ctx, tt.args.userID, tt.args.resourceOwner, tt.args.notifyType, tt.args.secretGenerator) + got, err := r.RequestSetPassword(tt.args.ctx, tt.args.userID, tt.args.resourceOwner, tt.args.notifyType, tt.args.secretGenerator, tt.args.authRequestID) if tt.res.err == nil { assert.NoError(t, err) } @@ -1093,7 +1136,7 @@ func TestCommandSide_RequestSetPassword(t *testing.T) { func TestCommandSide_PasswordCodeSent(t *testing.T) { type fields struct { - eventstore *eventstore.Eventstore + eventstore func(*testing.T) *eventstore.Eventstore } type args struct { ctx context.Context @@ -1112,9 +1155,7 @@ func TestCommandSide_PasswordCodeSent(t *testing.T) { { name: "userid missing, invalid argument error", fields: fields{ - eventstore: eventstoreExpect( - t, - ), + eventstore: expectEventstore(), }, args: args{ ctx: context.Background(), @@ -1127,8 +1168,7 @@ func TestCommandSide_PasswordCodeSent(t *testing.T) { { name: "user not existing, precondition error", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter(), ), }, @@ -1144,8 +1184,7 @@ func TestCommandSide_PasswordCodeSent(t *testing.T) { { name: "code sent, ok", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter( eventFromEventPusher( user.NewHumanAddedEvent(context.Background(), @@ -1186,7 +1225,7 @@ func TestCommandSide_PasswordCodeSent(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { r := &Commands{ - eventstore: tt.fields.eventstore, + eventstore: tt.fields.eventstore(t), } err := r.PasswordCodeSent(tt.args.ctx, tt.args.resourceOwner, tt.args.userID) if tt.res.err == nil { @@ -1201,7 +1240,7 @@ func TestCommandSide_PasswordCodeSent(t *testing.T) { func TestCommandSide_CheckPassword(t *testing.T) { type fields struct { - eventstore *eventstore.Eventstore + eventstore func(*testing.T) *eventstore.Eventstore userPasswordHasher *crypto.Hasher } type args struct { @@ -1224,9 +1263,7 @@ func TestCommandSide_CheckPassword(t *testing.T) { { name: "userid missing, invalid argument error", fields: fields{ - eventstore: eventstoreExpect( - t, - ), + eventstore: expectEventstore(), }, args: args{ ctx: context.Background(), @@ -1240,9 +1277,7 @@ func TestCommandSide_CheckPassword(t *testing.T) { { name: "password missing, invalid argument error", fields: fields{ - eventstore: eventstoreExpect( - t, - ), + eventstore: expectEventstore(), }, args: args{ ctx: context.Background(), @@ -1256,8 +1291,7 @@ func TestCommandSide_CheckPassword(t *testing.T) { { name: "login policy not found, precondition error", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter(), expectFilter(), ), @@ -1275,8 +1309,7 @@ func TestCommandSide_CheckPassword(t *testing.T) { { name: "login policy login password not allowed, precondition error", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter( eventFromEventPusher( org.NewLoginPolicyAddedEvent(context.Background(), @@ -1316,8 +1349,7 @@ func TestCommandSide_CheckPassword(t *testing.T) { { name: "user not existing, precondition error", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter( eventFromEventPusher( org.NewLoginPolicyAddedEvent(context.Background(), @@ -1358,8 +1390,7 @@ func TestCommandSide_CheckPassword(t *testing.T) { { name: "user locked, precondition error", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter( eventFromEventPusher( org.NewLoginPolicyAddedEvent(context.Background(), @@ -1420,8 +1451,7 @@ func TestCommandSide_CheckPassword(t *testing.T) { { name: "existing password empty, precondition error", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter( eventFromEventPusher( org.NewLoginPolicyAddedEvent(context.Background(), @@ -1478,8 +1508,7 @@ func TestCommandSide_CheckPassword(t *testing.T) { { name: "password not matching lockout policy not relevant, precondition error", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter( eventFromEventPusher( org.NewLoginPolicyAddedEvent(context.Background(), @@ -1562,8 +1591,7 @@ func TestCommandSide_CheckPassword(t *testing.T) { { name: "password not matching, max password attempts reached - user locked, precondition error", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter( eventFromEventPusher( org.NewLoginPolicyAddedEvent(context.Background(), @@ -1653,8 +1681,7 @@ func TestCommandSide_CheckPassword(t *testing.T) { { name: "check password, ok", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter( eventFromEventPusher( org.NewLoginPolicyAddedEvent(context.Background(), @@ -1734,8 +1761,7 @@ func TestCommandSide_CheckPassword(t *testing.T) { { name: "check password, ok, updated hash", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter( eventFromEventPusher( org.NewLoginPolicyAddedEvent(context.Background(), @@ -1820,8 +1846,7 @@ func TestCommandSide_CheckPassword(t *testing.T) { { name: "check password ok, locked in the mean time", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter( eventFromEventPusher( org.NewLoginPolicyAddedEvent(context.Background(), @@ -1900,8 +1925,7 @@ func TestCommandSide_CheckPassword(t *testing.T) { { name: "regression test old version event", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter( eventFromEventPusher( org.NewLoginPolicyAddedEvent(context.Background(), @@ -1996,7 +2020,7 @@ func TestCommandSide_CheckPassword(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { r := &Commands{ - eventstore: tt.fields.eventstore, + eventstore: tt.fields.eventstore(t), userPasswordHasher: tt.fields.userPasswordHasher, } err := r.HumanCheckPassword(tt.args.ctx, tt.args.resourceOwner, tt.args.userID, tt.args.password, tt.args.authReq, tt.args.lockoutPolicy) diff --git a/internal/command/user_human_test.go b/internal/command/user_human_test.go index 689d8419b..7010d4175 100644 --- a/internal/command/user_human_test.go +++ b/internal/command/user_human_test.go @@ -240,6 +240,7 @@ func TestCommandSide_AddHuman(t *testing.T) { Crypted: []byte("userinit"), }, time.Hour*1, + "", ), ), ), @@ -307,6 +308,7 @@ func TestCommandSide_AddHuman(t *testing.T) { Crypted: []byte("userinit"), }, time.Hour*1, + "", ), ), ), @@ -375,6 +377,7 @@ func TestCommandSide_AddHuman(t *testing.T) { Crypted: []byte("userinit"), }, time.Hour*1, + "", ), ), ), @@ -444,6 +447,7 @@ func TestCommandSide_AddHuman(t *testing.T) { Crypted: []byte("userinit"), }, 1*time.Hour, + "", ), ), ), @@ -1052,6 +1056,7 @@ func TestCommandSide_AddHuman(t *testing.T) { Crypted: []byte("userinit"), }, 1*time.Hour, + "", ), user.NewHumanPhoneVerifiedEvent( context.Background(), @@ -1193,6 +1198,7 @@ func TestCommandSide_AddHuman(t *testing.T) { Crypted: []byte("userinit"), }, 1*time.Hour, + "", ), user.NewMetadataSetEvent( context.Background(), @@ -1469,6 +1475,7 @@ func TestCommandSide_ImportHuman(t *testing.T) { Crypted: []byte("a"), }, time.Hour*1, + "", ), ), ), @@ -1840,6 +1847,7 @@ func TestCommandSide_ImportHuman(t *testing.T) { Crypted: []byte("a"), }, time.Hour*1, + "", ), user.NewHumanPhoneCodeAddedEvent(context.Background(), &user.NewAggregate("user1", "org1").Aggregate, @@ -1941,6 +1949,7 @@ func TestCommandSide_ImportHuman(t *testing.T) { Crypted: []byte("a"), }, time.Hour*1, + "", ), user.NewHumanPhoneVerifiedEvent(context.Background(), &user.NewAggregate("user1", "org1").Aggregate), @@ -2036,6 +2045,7 @@ func TestCommandSide_ImportHuman(t *testing.T) { Crypted: []byte("a"), }, time.Hour*1, + "", ), ), ), @@ -2121,6 +2131,7 @@ func TestCommandSide_ImportHuman(t *testing.T) { Crypted: []byte("a"), }, time.Hour*1, + "", ), ), ), @@ -2479,1498 +2490,6 @@ func TestCommandSide_ImportHuman(t *testing.T) { } } -func TestCommandSide_RegisterHuman(t *testing.T) { - type fields struct { - eventstore *eventstore.Eventstore - idGenerator id.Generator - userPasswordHasher *crypto.Hasher - } - type args struct { - ctx context.Context - orgID string - human *domain.Human - link *domain.UserIDPLink - orgMemberRoles []string - secretGenerator crypto.Generator - } - type res struct { - want *domain.Human - err func(error) bool - } - tests := []struct { - name string - fields fields - args args - res res - }{ - { - name: "orgid missing, invalid argument error", - fields: fields{ - eventstore: eventstoreExpect( - t, - ), - }, - args: args{ - ctx: context.Background(), - orgID: "", - human: &domain.Human{ - Username: "username", - Profile: &domain.Profile{ - FirstName: "firstname", - LastName: "lastname", - }, - Email: &domain.Email{ - EmailAddress: "email@test.ch", - }, - }, - }, - res: res{ - err: zerrors.IsErrorInvalidArgument, - }, - }, - { - name: "org policy not found, precondition error", - fields: fields{ - eventstore: eventstoreExpect( - t, - expectFilter(), - expectFilter(), - ), - }, - args: args{ - ctx: context.Background(), - orgID: "org1", - human: &domain.Human{ - Username: "username", - Profile: &domain.Profile{ - FirstName: "firstname", - LastName: "lastname", - }, - Email: &domain.Email{ - EmailAddress: "email@test.ch", - }, - Password: &domain.Password{ - SecretString: "password", - }, - }, - }, - res: res{ - err: zerrors.IsPreconditionFailed, - }, - }, - { - name: "password policy not found, precondition error", - fields: fields{ - eventstore: eventstoreExpect( - t, - expectFilter( - eventFromEventPusher( - org.NewDomainPolicyAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - true, - true, - true, - ), - ), - ), - expectFilter(), - expectFilter(), - ), - }, - args: args{ - ctx: context.Background(), - orgID: "org1", - human: &domain.Human{ - Username: "username", - Profile: &domain.Profile{ - FirstName: "firstname", - LastName: "lastname", - }, - Email: &domain.Email{ - EmailAddress: "email@test.ch", - }, - Password: &domain.Password{ - SecretString: "password", - }, - }, - }, - res: res{ - err: zerrors.IsPreconditionFailed, - }, - }, - { - name: "login policy not found, precondition error", - fields: fields{ - eventstore: eventstoreExpect( - t, - expectFilter( - eventFromEventPusher( - org.NewDomainPolicyAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - true, - true, - true, - ), - ), - ), - expectFilter( - eventFromEventPusher( - org.NewPasswordComplexityPolicyAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - 1, - false, - false, - false, - false, - ), - ), - ), - expectFilter(), - expectFilter(), - ), - }, - args: args{ - ctx: context.Background(), - orgID: "org1", - human: &domain.Human{ - Username: "username", - Profile: &domain.Profile{ - FirstName: "firstname", - }, - Password: &domain.Password{ - SecretString: "password", - }, - }, - }, - res: res{ - err: zerrors.IsPreconditionFailed, - }, - }, - { - name: "login policy registration not allowed, precondition error", - fields: fields{ - eventstore: eventstoreExpect( - t, - expectFilter( - eventFromEventPusher( - org.NewDomainPolicyAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - true, - true, - true, - ), - ), - ), - expectFilter( - eventFromEventPusher( - org.NewPasswordComplexityPolicyAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - 1, - false, - false, - false, - false, - ), - ), - ), - expectFilter( - eventFromEventPusher( - org.NewLoginPolicyAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - false, - false, - false, - false, - false, - false, - false, - false, - false, - false, - domain.PasswordlessTypeNotAllowed, - "", - time.Hour*1, - time.Hour*2, - time.Hour*3, - time.Hour*4, - time.Hour*5, - ), - ), - ), - ), - }, - args: args{ - ctx: context.Background(), - orgID: "org1", - human: &domain.Human{ - Username: "username", - Profile: &domain.Profile{ - FirstName: "firstname", - }, - Password: &domain.Password{ - SecretString: "password", - }, - }, - }, - res: res{ - err: zerrors.IsPreconditionFailed, - }, - }, - { - name: "user invalid, invalid argument error", - fields: fields{ - eventstore: eventstoreExpect( - t, - expectFilter( - eventFromEventPusher( - org.NewDomainPolicyAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - true, - true, - true, - ), - ), - ), - expectFilter( - eventFromEventPusher( - org.NewPasswordComplexityPolicyAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - 1, - false, - false, - false, - false, - ), - ), - ), - expectFilter( - eventFromEventPusher( - org.NewLoginPolicyAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - false, - true, - false, - false, - false, - false, - false, - false, - false, - false, - domain.PasswordlessTypeNotAllowed, - "", - time.Hour*1, - time.Hour*2, - time.Hour*3, - time.Hour*4, - time.Hour*5, - ), - ), - ), - ), - }, - args: args{ - ctx: context.Background(), - orgID: "org1", - human: &domain.Human{ - Username: "username", - Profile: &domain.Profile{ - FirstName: "firstname", - }, - Password: &domain.Password{ - SecretString: "password", - }, - }, - }, - res: res{ - err: zerrors.IsErrorInvalidArgument, - }, - }, - { - name: "email domain reserved, invalid argument error", - fields: fields{ - eventstore: eventstoreExpect( - t, - expectFilter( - eventFromEventPusher( - org.NewDomainPolicyAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - false, - true, - true, - ), - ), - ), - expectFilter( - eventFromEventPusher( - org.NewPasswordComplexityPolicyAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - 1, - false, - false, - false, - false, - ), - ), - ), - expectFilter( - eventFromEventPusher( - org.NewLoginPolicyAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - false, - true, - false, - false, - false, - false, - false, - false, - false, - false, - domain.PasswordlessTypeNotAllowed, - "", - time.Hour*1, - time.Hour*2, - time.Hour*3, - time.Hour*4, - time.Hour*5, - ), - ), - ), - expectFilter( - eventFromEventPusher( - org.NewDomainAddedEvent(context.Background(), - &org.NewAggregate("org2").Aggregate, - "test.ch", - ), - ), - eventFromEventPusher( - org.NewDomainVerifiedEvent(context.Background(), - &org.NewAggregate("org2").Aggregate, - "test.ch", - ), - ), - ), - ), - }, - args: args{ - ctx: context.Background(), - orgID: "org1", - human: &domain.Human{ - Password: &domain.Password{ - SecretString: "password", - }, - Profile: &domain.Profile{ - FirstName: "firstname", - LastName: "lastname", - }, - Email: &domain.Email{ - EmailAddress: "email@test.ch", - }, - }, - }, - res: res{ - err: zerrors.IsErrorInvalidArgument, - }, - }, - { - name: "email domain reserved, same org, ok", - fields: fields{ - eventstore: eventstoreExpect( - t, - expectFilter( - eventFromEventPusher( - org.NewDomainPolicyAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - false, - true, - true, - ), - ), - ), - expectFilter( - eventFromEventPusher( - org.NewPasswordComplexityPolicyAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - 1, - false, - false, - false, - false, - ), - ), - ), - expectFilter( - eventFromEventPusher( - org.NewLoginPolicyAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - false, - true, - false, - false, - false, - false, - false, - false, - false, - false, - domain.PasswordlessTypeNotAllowed, - "", - time.Hour*1, - time.Hour*2, - time.Hour*3, - time.Hour*4, - time.Hour*5, - ), - ), - ), - expectFilter( - eventFromEventPusher( - org.NewDomainAddedEvent(context.Background(), - &org.NewAggregate("org2").Aggregate, - "test.ch", - ), - ), - eventFromEventPusher( - org.NewDomainVerifiedEvent(context.Background(), - &org.NewAggregate("org2").Aggregate, - "test.ch", - ), - ), - eventFromEventPusher( - org.NewDomainRemovedEvent(context.Background(), - &org.NewAggregate("org2").Aggregate, - "test.ch", - true, - ), - ), - eventFromEventPusher( - org.NewDomainAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - "test.ch", - ), - ), - eventFromEventPusher( - org.NewDomainVerifiedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - "test.ch", - ), - ), - ), - expectPush( - newRegisterHumanEvent("email@test.ch", "$plain$x$password", false, false, "", AllowedLanguage), - user.NewHumanInitialCodeAddedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("a"), - }, - time.Hour*1, - ), - ), - ), - idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"), - userPasswordHasher: mockPasswordHasher("x"), - }, - args: args{ - ctx: context.Background(), - orgID: "org1", - human: &domain.Human{ - Password: &domain.Password{ - SecretString: "password", - }, - Profile: &domain.Profile{ - FirstName: "firstname", - LastName: "lastname", - PreferredLanguage: AllowedLanguage, - }, - Email: &domain.Email{ - EmailAddress: "email@test.ch", - }, - }, - secretGenerator: GetMockSecretGenerator(t), - }, - res: res{ - want: &domain.Human{ - ObjectRoot: models.ObjectRoot{ - AggregateID: "user1", - ResourceOwner: "org1", - }, - Username: "email@test.ch", - Profile: &domain.Profile{ - FirstName: "firstname", - LastName: "lastname", - DisplayName: "firstname lastname", - PreferredLanguage: AllowedLanguage, - }, - Email: &domain.Email{ - EmailAddress: "email@test.ch", - }, - State: domain.UserStateInitial, - }, - }, - }, - { - name: "username without @, ok", - fields: fields{ - eventstore: eventstoreExpect( - t, - expectFilter( - eventFromEventPusher( - org.NewDomainPolicyAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - false, - true, - true, - ), - ), - ), - expectFilter( - eventFromEventPusher( - org.NewPasswordComplexityPolicyAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - 1, - false, - false, - false, - false, - ), - ), - ), - expectFilter( - eventFromEventPusher( - org.NewLoginPolicyAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - false, - true, - false, - false, - false, - false, - false, - false, - false, - false, - domain.PasswordlessTypeNotAllowed, - "", - time.Hour*1, - time.Hour*2, - time.Hour*3, - time.Hour*4, - time.Hour*5, - ), - ), - ), - expectPush( - newRegisterHumanEvent("username", "$plain$x$password", false, false, "", AllowedLanguage), - user.NewHumanInitialCodeAddedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("a"), - }, - time.Hour*1, - ), - ), - ), - idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"), - userPasswordHasher: mockPasswordHasher("x"), - }, - args: args{ - ctx: context.Background(), - orgID: "org1", - human: &domain.Human{ - Password: &domain.Password{ - SecretString: "password", - }, - Profile: &domain.Profile{ - FirstName: "firstname", - LastName: "lastname", - PreferredLanguage: AllowedLanguage, - }, - Email: &domain.Email{ - EmailAddress: "email@test.ch", - }, - Username: "username", - }, - secretGenerator: GetMockSecretGenerator(t), - }, - res: res{ - want: &domain.Human{ - ObjectRoot: models.ObjectRoot{ - AggregateID: "user1", - ResourceOwner: "org1", - }, - Username: "username", - Profile: &domain.Profile{ - FirstName: "firstname", - LastName: "lastname", - DisplayName: "firstname lastname", - PreferredLanguage: AllowedLanguage, - }, - Email: &domain.Email{ - EmailAddress: "email@test.ch", - }, - State: domain.UserStateInitial, - }, - }, - }, - { - name: "add human (with password and initial code), ok", - fields: fields{ - eventstore: eventstoreExpect( - t, - expectFilter( - eventFromEventPusher( - org.NewDomainPolicyAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - true, - true, - true, - ), - ), - ), - expectFilter( - eventFromEventPusher( - org.NewPasswordComplexityPolicyAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - 1, - false, - false, - false, - false, - ), - ), - ), - expectFilter( - eventFromEventPusher( - org.NewLoginPolicyAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - false, - true, - false, - false, - false, - false, - false, - false, - false, - false, - domain.PasswordlessTypeNotAllowed, - "", - time.Hour*1, - time.Hour*2, - time.Hour*3, - time.Hour*4, - time.Hour*5, - ), - ), - ), - expectPush( - newRegisterHumanEvent("username", "$plain$x$password", false, true, "", AllowedLanguage), - user.NewHumanInitialCodeAddedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("a"), - }, - time.Hour*1, - ), - ), - ), - idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"), - userPasswordHasher: mockPasswordHasher("x"), - }, - args: args{ - ctx: context.Background(), - orgID: "org1", - human: &domain.Human{ - Username: "username", - Password: &domain.Password{ - SecretString: "password", - }, - Profile: &domain.Profile{ - FirstName: "firstname", - LastName: "lastname", - PreferredLanguage: AllowedLanguage, - }, - Email: &domain.Email{ - EmailAddress: "email@test.ch", - }, - }, - secretGenerator: GetMockSecretGenerator(t), - }, - res: res{ - want: &domain.Human{ - ObjectRoot: models.ObjectRoot{ - AggregateID: "user1", - ResourceOwner: "org1", - }, - Username: "username", - Profile: &domain.Profile{ - FirstName: "firstname", - LastName: "lastname", - DisplayName: "firstname lastname", - PreferredLanguage: AllowedLanguage, - }, - Email: &domain.Email{ - EmailAddress: "email@test.ch", - }, - State: domain.UserStateInitial, - }, - }, - }, - { - name: "add human email verified, ok", - fields: fields{ - eventstore: eventstoreExpect( - t, - expectFilter( - eventFromEventPusher( - org.NewDomainPolicyAddedEvent(context.Background(), - &user.NewAggregate("org1", "org1").Aggregate, - true, - true, - true, - ), - ), - ), - expectFilter( - eventFromEventPusher( - org.NewPasswordComplexityPolicyAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - 1, - false, - false, - false, - false, - ), - ), - ), - expectFilter( - eventFromEventPusher( - org.NewLoginPolicyAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - false, - true, - false, - false, - false, - false, - false, - false, - false, - false, - domain.PasswordlessTypeNotAllowed, - "", - time.Hour*1, - time.Hour*2, - time.Hour*3, - time.Hour*4, - time.Hour*5, - ), - ), - ), - expectPush( - newRegisterHumanEvent("username", "$plain$x$password", false, true, "", AllowedLanguage), - user.NewHumanEmailVerifiedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate), - ), - ), - idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"), - userPasswordHasher: mockPasswordHasher("x"), - }, - args: args{ - ctx: context.Background(), - orgID: "org1", - human: &domain.Human{ - Username: "username", - Password: &domain.Password{ - SecretString: "password", - }, - Profile: &domain.Profile{ - FirstName: "firstname", - LastName: "lastname", - PreferredLanguage: AllowedLanguage, - }, - Email: &domain.Email{ - EmailAddress: "email@test.ch", - IsEmailVerified: true, - }, - }, - secretGenerator: GetMockSecretGenerator(t), - }, - res: res{ - want: &domain.Human{ - ObjectRoot: models.ObjectRoot{ - AggregateID: "user1", - ResourceOwner: "org1", - }, - Username: "username", - Profile: &domain.Profile{ - FirstName: "firstname", - LastName: "lastname", - DisplayName: "firstname lastname", - PreferredLanguage: AllowedLanguage, - }, - Email: &domain.Email{ - EmailAddress: "email@test.ch", - IsEmailVerified: true, - }, - State: domain.UserStateActive, - }, - }, - }, - { - name: "add human (with phone), ok", - fields: fields{ - eventstore: eventstoreExpect( - t, - expectFilter( - eventFromEventPusher( - org.NewDomainPolicyAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - true, - true, - true, - ), - ), - ), - expectFilter( - eventFromEventPusher( - org.NewPasswordComplexityPolicyAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - 1, - false, - false, - false, - false, - ), - ), - ), - expectFilter( - eventFromEventPusher( - org.NewLoginPolicyAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - false, - true, - false, - false, - false, - false, - false, - false, - false, - false, - domain.PasswordlessTypeNotAllowed, - "", - time.Hour*1, - time.Hour*2, - time.Hour*3, - time.Hour*4, - time.Hour*5, - ), - ), - ), - expectPush( - newRegisterHumanEvent("username", "$plain$x$password", false, true, "+41711234567", AllowedLanguage), - user.NewHumanInitialCodeAddedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("a"), - }, - time.Hour*1, - ), - user.NewHumanPhoneCodeAddedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("a"), - }, - time.Hour*1, - ), - ), - ), - idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"), - userPasswordHasher: mockPasswordHasher("x"), - }, - args: args{ - ctx: context.Background(), - orgID: "org1", - human: &domain.Human{ - Username: "username", - Profile: &domain.Profile{ - FirstName: "firstname", - LastName: "lastname", - PreferredLanguage: AllowedLanguage, - }, - Email: &domain.Email{ - EmailAddress: "email@test.ch", - }, - Phone: &domain.Phone{ - PhoneNumber: "+41711234567", - }, - Password: &domain.Password{ - SecretString: "password", - }, - }, - secretGenerator: GetMockSecretGenerator(t), - }, - res: res{ - want: &domain.Human{ - ObjectRoot: models.ObjectRoot{ - AggregateID: "user1", - ResourceOwner: "org1", - }, - Username: "username", - Profile: &domain.Profile{ - FirstName: "firstname", - LastName: "lastname", - DisplayName: "firstname lastname", - PreferredLanguage: AllowedLanguage, - }, - Email: &domain.Email{ - EmailAddress: "email@test.ch", - }, - Phone: &domain.Phone{ - PhoneNumber: "+41711234567", - }, - State: domain.UserStateInitial, - }, - }, - }, - { - name: "add human (with verified phone), ok", - fields: fields{ - eventstore: eventstoreExpect( - t, - expectFilter( - eventFromEventPusher( - org.NewDomainPolicyAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - true, - true, - true, - ), - ), - ), - expectFilter( - eventFromEventPusher( - org.NewPasswordComplexityPolicyAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - 1, - false, - false, - false, - false, - ), - ), - ), - expectFilter( - eventFromEventPusher( - org.NewLoginPolicyAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - false, - true, - false, - false, - false, - false, - false, - false, - false, - false, - domain.PasswordlessTypeNotAllowed, - "", - time.Hour*1, - time.Hour*2, - time.Hour*3, - time.Hour*4, - time.Hour*5, - ), - ), - ), - expectPush( - newRegisterHumanEvent("username", "$plain$x$password", false, true, "+41711234567", AllowedLanguage), - user.NewHumanInitialCodeAddedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("a"), - }, - time.Hour*1, - ), - user.NewHumanPhoneVerifiedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - ), - ), - ), - idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"), - userPasswordHasher: mockPasswordHasher("x"), - }, - args: args{ - ctx: context.Background(), - orgID: "org1", - human: &domain.Human{ - Username: "username", - Profile: &domain.Profile{ - FirstName: "firstname", - LastName: "lastname", - PreferredLanguage: AllowedLanguage, - }, - Email: &domain.Email{ - EmailAddress: "email@test.ch", - }, - Phone: &domain.Phone{ - PhoneNumber: "+41711234567", - IsPhoneVerified: true, - }, - Password: &domain.Password{ - SecretString: "password", - }, - }, - secretGenerator: GetMockSecretGenerator(t), - }, - res: res{ - want: &domain.Human{ - ObjectRoot: models.ObjectRoot{ - AggregateID: "user1", - ResourceOwner: "org1", - }, - Username: "username", - Profile: &domain.Profile{ - FirstName: "firstname", - LastName: "lastname", - DisplayName: "firstname lastname", - PreferredLanguage: AllowedLanguage, - }, - Email: &domain.Email{ - EmailAddress: "email@test.ch", - }, - Phone: &domain.Phone{ - PhoneNumber: "+41711234567", - }, - State: domain.UserStateInitial, - }, - }, - }, - { - name: "add human (with unsupported preferred language), ok", - fields: fields{ - eventstore: eventstoreExpect( - t, - expectFilter( - eventFromEventPusher( - org.NewDomainPolicyAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - true, - true, - true, - ), - ), - ), - expectFilter( - eventFromEventPusher( - org.NewPasswordComplexityPolicyAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - 1, - false, - false, - false, - false, - ), - ), - ), - expectFilter( - eventFromEventPusher( - org.NewLoginPolicyAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - false, - true, - false, - false, - false, - false, - false, - false, - false, - false, - domain.PasswordlessTypeNotAllowed, - "", - time.Hour*1, - time.Hour*2, - time.Hour*3, - time.Hour*4, - time.Hour*5, - ), - ), - ), - expectPush( - newRegisterHumanEvent("username", "$plain$x$password", false, true, "", UnsupportedLanguage), - user.NewHumanInitialCodeAddedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("a"), - }, - time.Hour*1, - ), - ), - ), - idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"), - userPasswordHasher: mockPasswordHasher("x"), - }, - args: args{ - ctx: context.Background(), - orgID: "org1", - human: &domain.Human{ - Username: "username", - Profile: &domain.Profile{ - FirstName: "firstname", - LastName: "lastname", - PreferredLanguage: UnsupportedLanguage, - }, - Email: &domain.Email{ - EmailAddress: "email@test.ch", - }, - Password: &domain.Password{ - SecretString: "password", - }, - }, - secretGenerator: GetMockSecretGenerator(t), - }, - res: res{ - want: &domain.Human{ - ObjectRoot: models.ObjectRoot{ - AggregateID: "user1", - ResourceOwner: "org1", - }, - Username: "username", - Profile: &domain.Profile{ - FirstName: "firstname", - LastName: "lastname", - DisplayName: "firstname lastname", - PreferredLanguage: UnsupportedLanguage, - }, - Email: &domain.Email{ - EmailAddress: "email@test.ch", - }, - State: domain.UserStateInitial, - }, - }, - }, - { - name: "add human (with undefined preferred language), ok", - fields: fields{ - eventstore: eventstoreExpect( - t, - expectFilter( - eventFromEventPusher( - org.NewDomainPolicyAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - true, - true, - true, - ), - ), - ), - expectFilter( - eventFromEventPusher( - org.NewPasswordComplexityPolicyAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - 1, - false, - false, - false, - false, - ), - ), - ), - expectFilter( - eventFromEventPusher( - org.NewLoginPolicyAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - false, - true, - false, - false, - false, - false, - false, - false, - false, - false, - domain.PasswordlessTypeNotAllowed, - "", - time.Hour*1, - time.Hour*2, - time.Hour*3, - time.Hour*4, - time.Hour*5, - ), - ), - ), - expectPush( - newRegisterHumanEvent("username", "$plain$x$password", false, true, "", language.Und), - user.NewHumanInitialCodeAddedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - &crypto.CryptoValue{ - CryptoType: crypto.TypeEncryption, - Algorithm: "enc", - KeyID: "id", - Crypted: []byte("a"), - }, - time.Hour*1, - ), - ), - ), - idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"), - userPasswordHasher: mockPasswordHasher("x"), - }, - args: args{ - ctx: context.Background(), - orgID: "org1", - human: &domain.Human{ - Username: "username", - Profile: &domain.Profile{ - FirstName: "firstname", - LastName: "lastname", - }, - Email: &domain.Email{ - EmailAddress: "email@test.ch", - }, - Password: &domain.Password{ - SecretString: "password", - }, - }, - secretGenerator: GetMockSecretGenerator(t), - }, - res: res{ - want: &domain.Human{ - ObjectRoot: models.ObjectRoot{ - AggregateID: "user1", - ResourceOwner: "org1", - }, - Username: "username", - Profile: &domain.Profile{ - FirstName: "firstname", - LastName: "lastname", - DisplayName: "firstname lastname", - }, - Email: &domain.Email{ - EmailAddress: "email@test.ch", - }, - State: domain.UserStateInitial, - }, - }, - }, - { - name: "add with idp link, email verified, ok", - fields: fields{ - eventstore: eventstoreExpect( - t, - expectFilter( - eventFromEventPusher( - org.NewDomainPolicyAddedEvent(context.Background(), - &user.NewAggregate("org1", "org1").Aggregate, - true, - true, - true, - ), - ), - ), - expectFilter( - eventFromEventPusher( - org.NewPasswordComplexityPolicyAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - 1, - false, - false, - false, - false, - ), - ), - ), - expectFilter( - eventFromEventPusher( - org.NewLoginPolicyAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - false, - true, - true, - false, - false, - false, - false, - false, - false, - false, - domain.PasswordlessTypeNotAllowed, - "", - time.Hour*1, - time.Hour*2, - time.Hour*3, - time.Hour*4, - time.Hour*5, - ), - ), - ), - expectFilter( - eventFromEventPusher( - org.NewIDPConfigAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - "idpID", - "name", - domain.IDPConfigTypeOIDC, - domain.IDPConfigStylingTypeUnspecified, - false, - ), - ), - eventFromEventPusher( - org.NewIDPOIDCConfigAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - "clientID", - "idpID", - "issuer", - "authEndpoint", - "tokenEndpoint", - nil, - domain.OIDCMappingFieldUnspecified, - domain.OIDCMappingFieldUnspecified, - ), - ), - eventFromEventPusher( - org.NewIdentityProviderAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - "idpID", - domain.IdentityProviderTypeOrg, - ), - ), - ), - expectFilter( - eventFromEventPusher( - org.NewIDPConfigAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - "idpID", - "name", - domain.IDPConfigTypeOIDC, - domain.IDPConfigStylingTypeUnspecified, - false, - ), - ), - eventFromEventPusher( - org.NewIDPOIDCConfigAddedEvent(context.Background(), - &org.NewAggregate("org1").Aggregate, - "clientID", - "idpID", - "issuer", - "authEndpoint", - "tokenEndpoint", - nil, - domain.OIDCMappingFieldUnspecified, - domain.OIDCMappingFieldUnspecified, - ), - ), - ), - expectPush( - newRegisterHumanEvent("username", "$plain$x$password", false, true, "", AllowedLanguage), - user.NewUserIDPLinkAddedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - "idpID", - "displayName", - "externalID", - ), - user.NewHumanEmailVerifiedEvent(context.Background(), - &user.NewAggregate("user1", "org1").Aggregate, - ), - ), - ), - idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "user1"), - userPasswordHasher: mockPasswordHasher("x"), - }, - args: args{ - ctx: context.Background(), - orgID: "org1", - human: &domain.Human{ - Username: "username", - Password: &domain.Password{ - SecretString: "password", - }, - Profile: &domain.Profile{ - FirstName: "firstname", - LastName: "lastname", - PreferredLanguage: AllowedLanguage, - }, - Email: &domain.Email{ - EmailAddress: "email@test.ch", - IsEmailVerified: true, - }, - }, - link: &domain.UserIDPLink{ - IDPConfigID: "idpID", - ExternalUserID: "externalID", - DisplayName: "displayName", - }, - secretGenerator: GetMockSecretGenerator(t), - }, - res: res{ - want: &domain.Human{ - ObjectRoot: models.ObjectRoot{ - AggregateID: "user1", - ResourceOwner: "org1", - }, - Username: "username", - Profile: &domain.Profile{ - FirstName: "firstname", - LastName: "lastname", - DisplayName: "firstname lastname", - PreferredLanguage: AllowedLanguage, - }, - Email: &domain.Email{ - EmailAddress: "email@test.ch", - IsEmailVerified: true, - }, - State: domain.UserStateActive, - }, - }, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - r := &Commands{ - eventstore: tt.fields.eventstore, - idGenerator: tt.fields.idGenerator, - userPasswordHasher: tt.fields.userPasswordHasher, - } - got, err := r.RegisterHuman(tt.args.ctx, tt.args.orgID, tt.args.human, tt.args.link, tt.args.orgMemberRoles, tt.args.secretGenerator, tt.args.secretGenerator, tt.args.secretGenerator) - if tt.res.err == nil { - assert.NoError(t, err) - } - if tt.res.err != nil && !tt.res.err(err) { - t.Errorf("got wrong err: %v ", err) - } - if tt.res.err == nil { - assert.Equal(t, tt.res.want, got) - } - }) - } -} - func TestCommandSide_HumanMFASkip(t *testing.T) { type fields struct { eventstore *eventstore.Eventstore @@ -4311,6 +2830,7 @@ func newRegisterHumanEvent(username, password string, changeRequired, userLoginM domain.GenderUnspecified, "email@test.ch", userLoginMustBeUnique, + "", ) if password != "" { event.AddPasswordData(password, changeRequired) diff --git a/internal/command/user_test.go b/internal/command/user_test.go index 6538e0066..b906b7b5b 100644 --- a/internal/command/user_test.go +++ b/internal/command/user_test.go @@ -1797,6 +1797,7 @@ func TestExistsUser(t *testing.T) { domain.GenderFemale, "support@zitadel.com", true, + "userAgentID", ), }, nil }, diff --git a/internal/command/user_v2_email_test.go b/internal/command/user_v2_email_test.go index c26d0c749..fc8fc1c70 100644 --- a/internal/command/user_v2_email_test.go +++ b/internal/command/user_v2_email_test.go @@ -1838,7 +1838,7 @@ func TestCommands_verifyUserEmailWithGenerator(t *testing.T) { func TestCommands_NewUserEmailEvents(t *testing.T) { type fields struct { - eventstore *eventstore.Eventstore + eventstore func(*testing.T) *eventstore.Eventstore } type args struct { userID string @@ -1852,7 +1852,7 @@ func TestCommands_NewUserEmailEvents(t *testing.T) { { name: "missing userID", fields: fields{ - eventstore: eventstoreExpect(t), + eventstore: expectEventstore(), }, args: args{ userID: "", @@ -1862,7 +1862,7 @@ func TestCommands_NewUserEmailEvents(t *testing.T) { { name: "not found", fields: fields{ - eventstore: eventstoreExpect(t, expectFilter()), + eventstore: expectEventstore(expectFilter()), }, args: args{ userID: "user1", @@ -1872,8 +1872,7 @@ func TestCommands_NewUserEmailEvents(t *testing.T) { { name: "user not initialized", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter( eventFromEventPusher( user.NewHumanAddedEvent(context.Background(), @@ -1893,6 +1892,7 @@ func TestCommands_NewUserEmailEvents(t *testing.T) { user.NewHumanInitialCodeAddedEvent(context.Background(), &user.NewAggregate("user1", "org1").Aggregate, nil, time.Hour*1, + "", ), ), ), @@ -1907,7 +1907,7 @@ func TestCommands_NewUserEmailEvents(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { c := &Commands{ - eventstore: tt.fields.eventstore, + eventstore: tt.fields.eventstore(t), } _, err := c.NewUserEmailEvents(context.Background(), tt.args.userID) require.ErrorIs(t, err, tt.wantErr) diff --git a/internal/command/user_v2_human.go b/internal/command/user_v2_human.go index ab05492b5..dfd8470a6 100644 --- a/internal/command/user_v2_human.go +++ b/internal/command/user_v2_human.go @@ -131,8 +131,10 @@ func (c *Commands) AddUserHuman(ctx context.Context, resourceOwner string, human return zerrors.ThrowPreconditionFailed(nil, "COMMAND-7yiox1isql", "Errors.User.AlreadyExisting") } // check for permission to create user on resourceOwner - if err := c.checkPermission(ctx, domain.PermissionUserWrite, resourceOwner, human.ID); err != nil { - return err + if !human.Register { + if err := c.checkPermission(ctx, domain.PermissionUserWrite, resourceOwner, human.ID); err != nil { + return err + } } // add resourceowner for the events with the aggregate existingHuman.ResourceOwner = resourceOwner @@ -159,6 +161,7 @@ func (c *Commands) AddUserHuman(ctx context.Context, resourceOwner string, human human.Gender, human.Email.Address, domainPolicy.UserLoginMustBeDomain, + human.UserAgentID, ) } else { createCmd = user.NewHumanAddedEvent( diff --git a/internal/command/user_v2_human_test.go b/internal/command/user_v2_human_test.go index 32a838b27..f0ea86c42 100644 --- a/internal/command/user_v2_human_test.go +++ b/internal/command/user_v2_human_test.go @@ -232,6 +232,7 @@ func TestCommandSide_AddUserHuman(t *testing.T) { domain.GenderUnspecified, "email@test.ch", true, + "userAgentID", ), user.NewHumanInitialCodeAddedEvent(context.Background(), &userAgg.Aggregate, @@ -242,6 +243,7 @@ func TestCommandSide_AddUserHuman(t *testing.T) { Crypted: []byte("userinit"), }, time.Hour*1, + "authRequestID", ), ), ), @@ -261,6 +263,8 @@ func TestCommandSide_AddUserHuman(t *testing.T) { }, PreferredLanguage: language.English, Register: true, + UserAgentID: "userAgentID", + AuthRequestID: "authRequestID", }, secretGenerator: GetMockSecretGenerator(t), allowInitMail: true, @@ -344,6 +348,7 @@ func TestCommandSide_AddUserHuman(t *testing.T) { Crypted: []byte("userinit"), }, time.Hour*1, + "", ), ), ), @@ -414,6 +419,7 @@ func TestCommandSide_AddUserHuman(t *testing.T) { Crypted: []byte("userinit"), }, 1*time.Hour, + "", ), ), ), @@ -1031,6 +1037,7 @@ func TestCommandSide_AddUserHuman(t *testing.T) { Crypted: []byte("userinit"), }, 1*time.Hour, + "", ), user.NewHumanPhoneVerifiedEvent( context.Background(), @@ -1174,6 +1181,7 @@ func TestCommandSide_AddUserHuman(t *testing.T) { Crypted: []byte("userinit"), }, 1*time.Hour, + "", ), user.NewMetadataSetEvent( context.Background(), @@ -1993,6 +2001,7 @@ func TestCommandSide_ChangeUserHuman(t *testing.T) { user.NewHumanInitialCodeAddedEvent(context.Background(), &userAgg.Aggregate, nil, time.Hour*1, + "", ), ), ), diff --git a/internal/command/user_v2_model_test.go b/internal/command/user_v2_model_test.go index 7c77e491f..73513a86a 100644 --- a/internal/command/user_v2_model_test.go +++ b/internal/command/user_v2_model_test.go @@ -167,6 +167,7 @@ func TestCommandSide_userExistsWriteModel(t *testing.T) { Crypted: []byte("a"), }, time.Hour*1, + "authRequestID", ), ), ), @@ -225,6 +226,7 @@ func TestCommandSide_userExistsWriteModel(t *testing.T) { Crypted: []byte("a"), }, time.Hour*1, + "authRequestID", ), ), eventFromEventPusher( @@ -280,6 +282,7 @@ func TestCommandSide_userExistsWriteModel(t *testing.T) { Crypted: []byte("a"), }, time.Hour*1, + "authRequestID", ), ), eventFromEventPusher( diff --git a/internal/command/user_v2_password_test.go b/internal/command/user_v2_password_test.go index b07fbb3de..a87037625 100644 --- a/internal/command/user_v2_password_test.go +++ b/internal/command/user_v2_password_test.go @@ -10,6 +10,7 @@ import ( "github.com/muhlemmer/gu" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "github.com/zitadel/zitadel/internal/crypto" "github.com/zitadel/zitadel/internal/domain" "github.com/zitadel/zitadel/internal/eventstore" @@ -69,7 +70,7 @@ func TestCommands_RequestPasswordReset(t *testing.T) { ), eventFromEventPusher( user.NewHumanInitialCodeAddedEvent(context.Background(), &user.NewAggregate("userID", "org1").Aggregate, - &crypto.CryptoValue{CryptoType: crypto.TypeEncryption, Algorithm: "enc", KeyID: "keyID", Crypted: []byte("code")}, 10*time.Second), + &crypto.CryptoValue{CryptoType: crypto.TypeEncryption, Algorithm: "enc", KeyID: "keyID", Crypted: []byte("code")}, 10*time.Second, ""), ), ), ), @@ -167,7 +168,7 @@ func TestCommands_RequestPasswordResetReturnCode(t *testing.T) { ), eventFromEventPusher( user.NewHumanInitialCodeAddedEvent(context.Background(), &user.NewAggregate("userID", "org1").Aggregate, - &crypto.CryptoValue{CryptoType: crypto.TypeEncryption, Algorithm: "enc", KeyID: "keyID", Crypted: []byte("code")}, 10*time.Second), + &crypto.CryptoValue{CryptoType: crypto.TypeEncryption, Algorithm: "enc", KeyID: "keyID", Crypted: []byte("code")}, 10*time.Second, ""), ), ), ), @@ -279,7 +280,7 @@ func TestCommands_RequestPasswordResetURLTemplate(t *testing.T) { ), eventFromEventPusher( user.NewHumanInitialCodeAddedEvent(context.Background(), &user.NewAggregate("userID", "org1").Aggregate, - &crypto.CryptoValue{CryptoType: crypto.TypeEncryption, Algorithm: "enc", KeyID: "keyID", Crypted: []byte("code")}, 10*time.Second), + &crypto.CryptoValue{CryptoType: crypto.TypeEncryption, Algorithm: "enc", KeyID: "keyID", Crypted: []byte("code")}, 10*time.Second, ""), ), ), ), @@ -390,7 +391,7 @@ func TestCommands_requestPasswordReset(t *testing.T) { ), eventFromEventPusher( user.NewHumanInitialCodeAddedEvent(context.Background(), &user.NewAggregate("userID", "org1").Aggregate, - &crypto.CryptoValue{CryptoType: crypto.TypeEncryption, Algorithm: "enc", KeyID: "keyID", Crypted: []byte("code")}, 10*time.Second), + &crypto.CryptoValue{CryptoType: crypto.TypeEncryption, Algorithm: "enc", KeyID: "keyID", Crypted: []byte("code")}, 10*time.Second, ""), ), ), ), diff --git a/internal/command/user_v2_test.go b/internal/command/user_v2_test.go index 15597b7dd..0ee9fe694 100644 --- a/internal/command/user_v2_test.go +++ b/internal/command/user_v2_test.go @@ -18,7 +18,7 @@ import ( func TestCommandSide_LockUserV2(t *testing.T) { type fields struct { - eventstore *eventstore.Eventstore + eventstore func(*testing.T) *eventstore.Eventstore checkPermission domain.PermissionCheck } type ( @@ -40,9 +40,7 @@ func TestCommandSide_LockUserV2(t *testing.T) { { name: "userid missing, invalid argument error", fields: fields{ - eventstore: eventstoreExpect( - t, - ), + eventstore: expectEventstore(), checkPermission: newMockPermissionCheckAllowed(), }, args: args{ @@ -58,8 +56,7 @@ func TestCommandSide_LockUserV2(t *testing.T) { { name: "user not existing, not found error", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter(), ), checkPermission: newMockPermissionCheckAllowed(), @@ -77,8 +74,7 @@ func TestCommandSide_LockUserV2(t *testing.T) { { name: "user already locked, precondition error", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter( eventFromEventPusher( user.NewHumanAddedEvent(context.Background(), @@ -116,8 +112,7 @@ func TestCommandSide_LockUserV2(t *testing.T) { { name: "user already locked, precondition error", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter( eventFromEventPusher( user.NewMachineAddedEvent(context.Background(), @@ -151,8 +146,7 @@ func TestCommandSide_LockUserV2(t *testing.T) { { name: "lock user, ok", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter( eventFromEventPusher( user.NewHumanAddedEvent(context.Background(), @@ -190,8 +184,7 @@ func TestCommandSide_LockUserV2(t *testing.T) { { name: "lock user, no permission", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter( eventFromEventPusher( user.NewHumanAddedEvent(context.Background(), @@ -224,8 +217,7 @@ func TestCommandSide_LockUserV2(t *testing.T) { { name: "lock user machine, ok", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter( eventFromEventPusher( user.NewMachineAddedEvent(context.Background(), @@ -260,7 +252,7 @@ func TestCommandSide_LockUserV2(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { r := &Commands{ - eventstore: tt.fields.eventstore, + eventstore: tt.fields.eventstore(t), checkPermission: tt.fields.checkPermission, } got, err := r.LockUserV2(tt.args.ctx, tt.args.userID) @@ -279,7 +271,7 @@ func TestCommandSide_LockUserV2(t *testing.T) { func TestCommandSide_UnlockUserV2(t *testing.T) { type fields struct { - eventstore *eventstore.Eventstore + eventstore func(*testing.T) *eventstore.Eventstore checkPermission domain.PermissionCheck } type ( @@ -301,9 +293,7 @@ func TestCommandSide_UnlockUserV2(t *testing.T) { { name: "userid missing, invalid argument error", fields: fields{ - eventstore: eventstoreExpect( - t, - ), + eventstore: expectEventstore(), checkPermission: newMockPermissionCheckAllowed(), }, args: args{ @@ -319,8 +309,7 @@ func TestCommandSide_UnlockUserV2(t *testing.T) { { name: "user not existing, not found error", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter(), ), checkPermission: newMockPermissionCheckAllowed(), @@ -338,8 +327,7 @@ func TestCommandSide_UnlockUserV2(t *testing.T) { { name: "user already active, precondition error", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter( eventFromEventPusher( user.NewHumanAddedEvent(context.Background(), @@ -372,8 +360,7 @@ func TestCommandSide_UnlockUserV2(t *testing.T) { { name: "user already active, precondition error", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter( user.NewMachineAddedEvent(context.Background(), &user.NewAggregate("user1", "org1").Aggregate, @@ -400,8 +387,7 @@ func TestCommandSide_UnlockUserV2(t *testing.T) { { name: "unlock user, ok", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter( eventFromEventPusher( user.NewHumanAddedEvent(context.Background(), @@ -443,8 +429,7 @@ func TestCommandSide_UnlockUserV2(t *testing.T) { { name: "unlock user, no permission", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter( eventFromEventPusher( user.NewHumanAddedEvent(context.Background(), @@ -481,8 +466,7 @@ func TestCommandSide_UnlockUserV2(t *testing.T) { { name: "unlock user machine, ok", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter( eventFromEventPusher( user.NewMachineAddedEvent(context.Background(), @@ -521,7 +505,7 @@ func TestCommandSide_UnlockUserV2(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { r := &Commands{ - eventstore: tt.fields.eventstore, + eventstore: tt.fields.eventstore(t), checkPermission: tt.fields.checkPermission, } got, err := r.UnlockUserV2(tt.args.ctx, tt.args.userID) @@ -540,7 +524,7 @@ func TestCommandSide_UnlockUserV2(t *testing.T) { func TestCommandSide_DeactivateUserV2(t *testing.T) { type fields struct { - eventstore *eventstore.Eventstore + eventstore func(*testing.T) *eventstore.Eventstore checkPermission domain.PermissionCheck } type ( @@ -562,9 +546,7 @@ func TestCommandSide_DeactivateUserV2(t *testing.T) { { name: "userid missing, invalid argument error", fields: fields{ - eventstore: eventstoreExpect( - t, - ), + eventstore: expectEventstore(), checkPermission: newMockPermissionCheckAllowed(), }, args: args{ @@ -580,8 +562,7 @@ func TestCommandSide_DeactivateUserV2(t *testing.T) { { name: "user not existing, not found error", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter(), ), checkPermission: newMockPermissionCheckAllowed(), @@ -599,8 +580,7 @@ func TestCommandSide_DeactivateUserV2(t *testing.T) { { name: "user initial, precondition error", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter( eventFromEventPusher( user.NewHumanAddedEvent(context.Background(), @@ -620,6 +600,7 @@ func TestCommandSide_DeactivateUserV2(t *testing.T) { user.NewHumanInitialCodeAddedEvent(context.Background(), &user.NewAggregate("user1", "org1").Aggregate, nil, time.Hour*1, + "", ), ), ), @@ -639,8 +620,7 @@ func TestCommandSide_DeactivateUserV2(t *testing.T) { { name: "user already inactive, precondition error", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter( eventFromEventPusher( user.NewHumanAddedEvent(context.Background(), @@ -678,8 +658,7 @@ func TestCommandSide_DeactivateUserV2(t *testing.T) { { name: "deactivate user, ok", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter( eventFromEventPusher( user.NewHumanAddedEvent(context.Background(), @@ -722,8 +701,7 @@ func TestCommandSide_DeactivateUserV2(t *testing.T) { { name: "deactivate user, no permission", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter( eventFromEventPusher( user.NewHumanAddedEvent(context.Background(), @@ -761,8 +739,7 @@ func TestCommandSide_DeactivateUserV2(t *testing.T) { { name: "user machine already inactive, precondition error", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter( eventFromEventPusher( user.NewMachineAddedEvent(context.Background(), @@ -796,8 +773,7 @@ func TestCommandSide_DeactivateUserV2(t *testing.T) { { name: "deactivate user machine, ok", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter( eventFromEventPusher( user.NewMachineAddedEvent(context.Background(), @@ -832,7 +808,7 @@ func TestCommandSide_DeactivateUserV2(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { r := &Commands{ - eventstore: tt.fields.eventstore, + eventstore: tt.fields.eventstore(t), checkPermission: tt.fields.checkPermission, } got, err := r.DeactivateUserV2(tt.args.ctx, tt.args.userID) @@ -851,7 +827,7 @@ func TestCommandSide_DeactivateUserV2(t *testing.T) { func TestCommandSide_ReactivateUserV2(t *testing.T) { type fields struct { - eventstore *eventstore.Eventstore + eventstore func(*testing.T) *eventstore.Eventstore checkPermission domain.PermissionCheck } type ( @@ -873,9 +849,7 @@ func TestCommandSide_ReactivateUserV2(t *testing.T) { { name: "userid missing, invalid argument error", fields: fields{ - eventstore: eventstoreExpect( - t, - ), + eventstore: expectEventstore(), checkPermission: newMockPermissionCheckAllowed(), }, args: args{ @@ -891,8 +865,7 @@ func TestCommandSide_ReactivateUserV2(t *testing.T) { { name: "user not existing, not found error", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter(), ), checkPermission: newMockPermissionCheckAllowed(), @@ -910,8 +883,7 @@ func TestCommandSide_ReactivateUserV2(t *testing.T) { { name: "user already active, precondition error", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter( eventFromEventPusher( user.NewHumanAddedEvent(context.Background(), @@ -944,8 +916,7 @@ func TestCommandSide_ReactivateUserV2(t *testing.T) { { name: "user machine already active, precondition error", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter( eventFromEventPusher( user.NewMachineAddedEvent(context.Background(), @@ -974,8 +945,7 @@ func TestCommandSide_ReactivateUserV2(t *testing.T) { { name: "reactivate user, ok", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter( eventFromEventPusher( user.NewHumanAddedEvent(context.Background(), @@ -1017,8 +987,7 @@ func TestCommandSide_ReactivateUserV2(t *testing.T) { { name: "reactivate user, no permission", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter( eventFromEventPusher( user.NewHumanAddedEvent(context.Background(), @@ -1055,8 +1024,7 @@ func TestCommandSide_ReactivateUserV2(t *testing.T) { { name: "reactivate user machine, ok", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter( eventFromEventPusher( user.NewMachineAddedEvent(context.Background(), @@ -1095,7 +1063,7 @@ func TestCommandSide_ReactivateUserV2(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { r := &Commands{ - eventstore: tt.fields.eventstore, + eventstore: tt.fields.eventstore(t), checkPermission: tt.fields.checkPermission, } got, err := r.ReactivateUserV2(tt.args.ctx, tt.args.userID) @@ -1114,7 +1082,7 @@ func TestCommandSide_ReactivateUserV2(t *testing.T) { func TestCommandSide_RemoveUserV2(t *testing.T) { type fields struct { - eventstore *eventstore.Eventstore + eventstore func(*testing.T) *eventstore.Eventstore checkPermission domain.PermissionCheck } type ( @@ -1138,9 +1106,7 @@ func TestCommandSide_RemoveUserV2(t *testing.T) { { name: "userid missing, invalid argument error", fields: fields{ - eventstore: eventstoreExpect( - t, - ), + eventstore: expectEventstore(), checkPermission: newMockPermissionCheckAllowed(), }, args: args{ @@ -1156,8 +1122,7 @@ func TestCommandSide_RemoveUserV2(t *testing.T) { { name: "user not existing, not found error", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter(), ), checkPermission: newMockPermissionCheckAllowed(), @@ -1175,8 +1140,7 @@ func TestCommandSide_RemoveUserV2(t *testing.T) { { name: "user removed, notfound error", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter( eventFromEventPusher( user.NewHumanAddedEvent(context.Background(), @@ -1217,8 +1181,7 @@ func TestCommandSide_RemoveUserV2(t *testing.T) { { name: "remove user, ok", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter( eventFromEventPusher( user.NewHumanAddedEvent(context.Background(), @@ -1269,8 +1232,7 @@ func TestCommandSide_RemoveUserV2(t *testing.T) { { name: "remove user, no permission", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter( eventFromEventPusher( user.NewHumanAddedEvent(context.Background(), @@ -1308,8 +1270,7 @@ func TestCommandSide_RemoveUserV2(t *testing.T) { { name: "user machine already removed, notfound error", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter( eventFromEventPusher( user.NewMachineAddedEvent(context.Background(), @@ -1346,8 +1307,7 @@ func TestCommandSide_RemoveUserV2(t *testing.T) { { name: "remove user machine, ok", fields: fields{ - eventstore: eventstoreExpect( - t, + eventstore: expectEventstore( expectFilter( eventFromEventPusher( user.NewMachineAddedEvent(context.Background(), @@ -1395,7 +1355,7 @@ func TestCommandSide_RemoveUserV2(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { r := &Commands{ - eventstore: tt.fields.eventstore, + eventstore: tt.fields.eventstore(t), checkPermission: tt.fields.checkPermission, } got, err := r.RemoveUserV2(tt.args.ctx, tt.args.userID, tt.args.cascadingMemberships, tt.args.grantIDs...) diff --git a/internal/notification/handlers/user_notifier.go b/internal/notification/handlers/user_notifier.go index f55345bff..fd3acc84a 100644 --- a/internal/notification/handlers/user_notifier.go +++ b/internal/notification/handlers/user_notifier.go @@ -169,7 +169,7 @@ func (u *userNotifier) reduceInitCodeAdded(event eventstore.Event) (*handler.Sta return err } err = types.SendEmail(ctx, u.channels, string(template.Template), translator, notifyUser, colors, e). - SendUserInitCode(ctx, notifyUser, code) + SendUserInitCode(ctx, notifyUser, code, e.AuthRequestID) if err != nil { return err } @@ -226,7 +226,7 @@ func (u *userNotifier) reduceEmailCodeAdded(event eventstore.Event) (*handler.St return err } err = types.SendEmail(ctx, u.channels, string(template.Template), translator, notifyUser, colors, e). - SendEmailVerificationCode(ctx, notifyUser, code, e.URLTemplate) + SendEmailVerificationCode(ctx, notifyUser, code, e.URLTemplate, e.AuthRequestID) if err != nil { return err } @@ -285,7 +285,7 @@ func (u *userNotifier) reducePasswordCodeAdded(event eventstore.Event) (*handler if e.NotificationType == domain.NotificationTypeSms { notify = types.SendSMSTwilio(ctx, u.channels, translator, notifyUser, colors, e) } - err = notify.SendPasswordCode(ctx, notifyUser, code, e.URLTemplate) + err = notify.SendPasswordCode(ctx, notifyUser, code, e.URLTemplate, e.AuthRequestID) if err != nil { return err } diff --git a/internal/notification/handlers/user_notifier_test.go b/internal/notification/handlers/user_notifier_test.go index 55751beb1..8b2155cc2 100644 --- a/internal/notification/handlers/user_notifier_test.go +++ b/internal/notification/handlers/user_notifier_test.go @@ -45,6 +45,7 @@ const ( externalSecure = false externalProtocol = "http" defaultOTPEmailTemplate = "/otp/verify?loginName={{.LoginName}}&code={{.Code}}" + authRequestID = "authRequestID" ) func Test_userNotifier_reduceInitCodeAdded(t *testing.T) { @@ -128,7 +129,7 @@ func Test_userNotifier_reduceInitCodeAdded(t *testing.T) { test: func(ctrl *gomock.Controller, queries *mock.MockQueries, commands *mock.MockCommands) (f fields, a args, w want) { givenTemplate := "{{.URL}}" testCode := "testcode" - expectContent := fmt.Sprintf("%s/ui/login/user/init?userID=%s&loginname=%s&code=%s&orgID=%s&passwordset=%t", eventOrigin, userID, preferredLoginName, testCode, orgID, false) + expectContent := fmt.Sprintf("%s/ui/login/user/init?authRequestID=%s&code=%s&loginname=%s&orgID=%s&passwordset=%t&userID=%s", eventOrigin, "", testCode, preferredLoginName, orgID, false, userID) w.message = messages.Email{ Recipients: []string{lastEmail}, Subject: expectMailSubject, @@ -162,7 +163,7 @@ func Test_userNotifier_reduceInitCodeAdded(t *testing.T) { test: func(ctrl *gomock.Controller, queries *mock.MockQueries, commands *mock.MockCommands) (f fields, a args, w want) { givenTemplate := "{{.URL}}" testCode := "testcode" - expectContent := fmt.Sprintf("%s://%s:%d/ui/login/user/init?userID=%s&loginname=%s&code=%s&orgID=%s&passwordset=%t", externalProtocol, instancePrimaryDomain, externalPort, userID, preferredLoginName, testCode, orgID, false) + expectContent := fmt.Sprintf("%s://%s:%d/ui/login/user/init?authRequestID=%s&code=%s&loginname=%s&orgID=%s&passwordset=%t&userID=%s", externalProtocol, instancePrimaryDomain, externalPort, "", testCode, preferredLoginName, orgID, false, userID) w.message = messages.Email{ Recipients: []string{lastEmail}, Subject: expectMailSubject, @@ -196,6 +197,46 @@ func Test_userNotifier_reduceInitCodeAdded(t *testing.T) { }, }, w }, + }, { + name: "button url without event trigger url with authRequestID", + test: func(ctrl *gomock.Controller, queries *mock.MockQueries, commands *mock.MockCommands) (f fields, a args, w want) { + givenTemplate := "{{.URL}}" + testCode := "testcode" + expectContent := fmt.Sprintf("%s://%s:%d/ui/login/user/init?authRequestID=%s&code=%s&loginname=%s&orgID=%s&passwordset=%t&userID=%s", externalProtocol, instancePrimaryDomain, externalPort, authRequestID, testCode, preferredLoginName, orgID, false, userID) + w.message = messages.Email{ + Recipients: []string{lastEmail}, + Subject: expectMailSubject, + Content: expectContent, + } + codeAlg, code := cryptoValue(t, ctrl, testCode) + queries.EXPECT().SearchInstanceDomains(gomock.Any(), gomock.Any()).Return(&query.InstanceDomains{ + Domains: []*query.InstanceDomain{{ + Domain: instancePrimaryDomain, + IsPrimary: true, + }}, + }, nil) + expectTemplateQueries(queries, givenTemplate) + commands.EXPECT().HumanInitCodeSent(gomock.Any(), orgID, userID).Return(nil) + return fields{ + queries: queries, + commands: commands, + es: eventstore.NewEventstore(&eventstore.Config{ + Querier: es_repo_mock.NewRepo(t).ExpectFilterEvents().MockQuerier, + }), + userDataCrypto: codeAlg, + }, args{ + event: &user.HumanInitialCodeAddedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(&repository.Event{ + AggregateID: userID, + ResourceOwner: sql.NullString{String: orgID}, + CreationDate: time.Now().UTC(), + }), + Code: code, + Expiry: time.Hour, + AuthRequestID: authRequestID, + }, + }, w + }, }} // TODO: Why don't we have an url template on user.HumanInitialCodeAddedEvent? for _, tt := range tests { @@ -305,7 +346,7 @@ func Test_userNotifier_reduceEmailCodeAdded(t *testing.T) { test: func(ctrl *gomock.Controller, queries *mock.MockQueries, commands *mock.MockCommands) (f fields, a args, w want) { givenTemplate := "{{.URL}}" testCode := "testcode" - expectContent := fmt.Sprintf("%s/ui/login/mail/verification?userID=%s&code=%s&orgID=%s", eventOrigin, userID, testCode, orgID) + expectContent := fmt.Sprintf("%s/ui/login/mail/verification?authRequestID=%s&code=%s&orgID=%s&userID=%s", eventOrigin, "", testCode, orgID, userID) w.message = messages.Email{ Recipients: []string{lastEmail}, Subject: expectMailSubject, @@ -342,7 +383,7 @@ func Test_userNotifier_reduceEmailCodeAdded(t *testing.T) { test: func(ctrl *gomock.Controller, queries *mock.MockQueries, commands *mock.MockCommands) (f fields, a args, w want) { givenTemplate := "{{.URL}}" testCode := "testcode" - expectContent := fmt.Sprintf("%s://%s:%d/ui/login/mail/verification?userID=%s&code=%s&orgID=%s", externalProtocol, instancePrimaryDomain, externalPort, userID, testCode, orgID) + expectContent := fmt.Sprintf("%s://%s:%d/ui/login/mail/verification?authRequestID=%s&code=%s&orgID=%s&userID=%s", externalProtocol, instancePrimaryDomain, externalPort, "", testCode, orgID, userID) w.message = messages.Email{ Recipients: []string{lastEmail}, Subject: expectMailSubject, @@ -378,6 +419,48 @@ func Test_userNotifier_reduceEmailCodeAdded(t *testing.T) { }, }, w }, + }, { + name: "button url without event trigger url with authRequestID", + test: func(ctrl *gomock.Controller, queries *mock.MockQueries, commands *mock.MockCommands) (f fields, a args, w want) { + givenTemplate := "{{.URL}}" + testCode := "testcode" + expectContent := fmt.Sprintf("%s://%s:%d/ui/login/mail/verification?authRequestID=%s&code=%s&orgID=%s&userID=%s", externalProtocol, instancePrimaryDomain, externalPort, authRequestID, testCode, orgID, userID) + w.message = messages.Email{ + Recipients: []string{lastEmail}, + Subject: expectMailSubject, + Content: expectContent, + } + codeAlg, code := cryptoValue(t, ctrl, testCode) + queries.EXPECT().SearchInstanceDomains(gomock.Any(), gomock.Any()).Return(&query.InstanceDomains{ + Domains: []*query.InstanceDomain{{ + Domain: instancePrimaryDomain, + IsPrimary: true, + }}, + }, nil) + expectTemplateQueries(queries, givenTemplate) + commands.EXPECT().HumanEmailVerificationCodeSent(gomock.Any(), orgID, userID).Return(nil) + return fields{ + queries: queries, + commands: commands, + es: eventstore.NewEventstore(&eventstore.Config{ + Querier: es_repo_mock.NewRepo(t).ExpectFilterEvents().MockQuerier, + }), + userDataCrypto: codeAlg, + }, args{ + event: &user.HumanEmailCodeAddedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(&repository.Event{ + AggregateID: userID, + ResourceOwner: sql.NullString{String: orgID}, + CreationDate: time.Now().UTC(), + }), + Code: code, + Expiry: time.Hour, + URLTemplate: "", + CodeReturned: false, + AuthRequestID: authRequestID, + }, + }, w + }, }, { name: "button url with url template and event trigger url", test: func(ctrl *gomock.Controller, queries *mock.MockQueries, commands *mock.MockCommands) (f fields, a args, w want) { @@ -524,7 +607,7 @@ func Test_userNotifier_reducePasswordCodeAdded(t *testing.T) { test: func(ctrl *gomock.Controller, queries *mock.MockQueries, commands *mock.MockCommands) (f fields, a args, w want) { givenTemplate := "{{.URL}}" testCode := "testcode" - expectContent := fmt.Sprintf("%s/ui/login/password/init?userID=%s&code=%s&orgID=%s", eventOrigin, userID, testCode, orgID) + expectContent := fmt.Sprintf("%s/ui/login/password/init?authRequestID=%s&code=%s&orgID=%s&userID=%s", eventOrigin, "", testCode, orgID, userID) w.message = messages.Email{ Recipients: []string{lastEmail}, Subject: expectMailSubject, @@ -561,7 +644,7 @@ func Test_userNotifier_reducePasswordCodeAdded(t *testing.T) { test: func(ctrl *gomock.Controller, queries *mock.MockQueries, commands *mock.MockCommands) (f fields, a args, w want) { givenTemplate := "{{.URL}}" testCode := "testcode" - expectContent := fmt.Sprintf("%s://%s:%d/ui/login/password/init?userID=%s&code=%s&orgID=%s", externalProtocol, instancePrimaryDomain, externalPort, userID, testCode, orgID) + expectContent := fmt.Sprintf("%s://%s:%d/ui/login/password/init?authRequestID=%s&code=%s&orgID=%s&userID=%s", externalProtocol, instancePrimaryDomain, externalPort, "", testCode, orgID, userID) w.message = messages.Email{ Recipients: []string{lastEmail}, Subject: expectMailSubject, @@ -597,6 +680,48 @@ func Test_userNotifier_reducePasswordCodeAdded(t *testing.T) { }, }, w }, + }, { + name: "button url without event trigger url with authRequestID", + test: func(ctrl *gomock.Controller, queries *mock.MockQueries, commands *mock.MockCommands) (f fields, a args, w want) { + givenTemplate := "{{.URL}}" + testCode := "testcode" + expectContent := fmt.Sprintf("%s://%s:%d/ui/login/password/init?authRequestID=%s&code=%s&orgID=%s&userID=%s", externalProtocol, instancePrimaryDomain, externalPort, authRequestID, testCode, orgID, userID) + w.message = messages.Email{ + Recipients: []string{lastEmail}, + Subject: expectMailSubject, + Content: expectContent, + } + codeAlg, code := cryptoValue(t, ctrl, testCode) + queries.EXPECT().SearchInstanceDomains(gomock.Any(), gomock.Any()).Return(&query.InstanceDomains{ + Domains: []*query.InstanceDomain{{ + Domain: instancePrimaryDomain, + IsPrimary: true, + }}, + }, nil) + expectTemplateQueries(queries, givenTemplate) + commands.EXPECT().PasswordCodeSent(gomock.Any(), orgID, userID).Return(nil) + return fields{ + queries: queries, + commands: commands, + es: eventstore.NewEventstore(&eventstore.Config{ + Querier: es_repo_mock.NewRepo(t).ExpectFilterEvents().MockQuerier, + }), + userDataCrypto: codeAlg, + }, args{ + event: &user.HumanPasswordCodeAddedEvent{ + BaseEvent: *eventstore.BaseEventFromRepo(&repository.Event{ + AggregateID: userID, + ResourceOwner: sql.NullString{String: orgID}, + CreationDate: time.Now().UTC(), + }), + Code: code, + Expiry: time.Hour, + URLTemplate: "", + CodeReturned: false, + AuthRequestID: authRequestID, + }, + }, w + }, }, { name: "button url with url template and event trigger url", test: func(ctrl *gomock.Controller, queries *mock.MockQueries, commands *mock.MockCommands) (f fields, a args, w want) { diff --git a/internal/notification/types/email_verification_code.go b/internal/notification/types/email_verification_code.go index b2f34acd6..912e846a9 100644 --- a/internal/notification/types/email_verification_code.go +++ b/internal/notification/types/email_verification_code.go @@ -10,10 +10,10 @@ import ( "github.com/zitadel/zitadel/internal/query" ) -func (notify Notify) SendEmailVerificationCode(ctx context.Context, user *query.NotifyUser, code string, urlTmpl string) error { +func (notify Notify) SendEmailVerificationCode(ctx context.Context, user *query.NotifyUser, code string, urlTmpl, authRequestID string) error { var url string if urlTmpl == "" { - url = login.MailVerificationLink(http_utils.ComposedOrigin(ctx), user.ID, code, user.ResourceOwner) + url = login.MailVerificationLink(http_utils.ComposedOrigin(ctx), user.ID, code, user.ResourceOwner, authRequestID) } else { var buf strings.Builder if err := domain.RenderConfirmURLTemplate(&buf, urlTmpl, user.ID, code, user.ResourceOwner); err != nil { diff --git a/internal/notification/types/email_verification_code_test.go b/internal/notification/types/email_verification_code_test.go index 38aeb987a..3f538b3fb 100644 --- a/internal/notification/types/email_verification_code_test.go +++ b/internal/notification/types/email_verification_code_test.go @@ -15,10 +15,11 @@ import ( func TestNotify_SendEmailVerificationCode(t *testing.T) { type args struct { - user *query.NotifyUser - origin string - code string - urlTmpl string + user *query.NotifyUser + origin string + code string + urlTmpl string + authRequestID string } tests := []struct { name string @@ -33,12 +34,13 @@ func TestNotify_SendEmailVerificationCode(t *testing.T) { ID: "user1", ResourceOwner: "org1", }, - origin: "https://example.com", - code: "123", - urlTmpl: "", + origin: "https://example.com", + code: "123", + urlTmpl: "", + authRequestID: "authRequestID", }, want: ¬ifyResult{ - url: "https://example.com/ui/login/mail/verification?userID=user1&code=123&orgID=org1", + url: "https://example.com/ui/login/mail/verification?authRequestID=authRequestID&code=123&orgID=org1&userID=user1", args: map[string]interface{}{"Code": "123"}, messageType: domain.VerifyEmailMessageType, allowUnverifiedNotificationChannel: true, @@ -51,9 +53,10 @@ func TestNotify_SendEmailVerificationCode(t *testing.T) { ID: "user1", ResourceOwner: "org1", }, - origin: "https://example.com", - code: "123", - urlTmpl: "{{", + origin: "https://example.com", + code: "123", + urlTmpl: "{{", + authRequestID: "authRequestID", }, want: ¬ifyResult{}, wantErr: zerrors.ThrowInvalidArgument(nil, "DOMAIN-oGh5e", "Errors.User.InvalidURLTemplate"), @@ -65,9 +68,10 @@ func TestNotify_SendEmailVerificationCode(t *testing.T) { ID: "user1", ResourceOwner: "org1", }, - origin: "https://example.com", - code: "123", - urlTmpl: "https://example.com/email/verify?userID={{.UserID}}&code={{.Code}}&orgID={{.OrgID}}", + origin: "https://example.com", + code: "123", + urlTmpl: "https://example.com/email/verify?userID={{.UserID}}&code={{.Code}}&orgID={{.OrgID}}", + authRequestID: "authRequestID", }, want: ¬ifyResult{ url: "https://example.com/email/verify?userID=user1&code=123&orgID=org1", @@ -80,7 +84,7 @@ func TestNotify_SendEmailVerificationCode(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, notify := mockNotify() - err := notify.SendEmailVerificationCode(http_utils.WithComposedOrigin(context.Background(), tt.args.origin), tt.args.user, tt.args.code, tt.args.urlTmpl) + err := notify.SendEmailVerificationCode(http_utils.WithComposedOrigin(context.Background(), tt.args.origin), tt.args.user, tt.args.code, tt.args.urlTmpl, tt.args.authRequestID) require.ErrorIs(t, err, tt.wantErr) assert.Equal(t, tt.want, got) }) diff --git a/internal/notification/types/init_code.go b/internal/notification/types/init_code.go index 0e30e991d..11ea75ab2 100644 --- a/internal/notification/types/init_code.go +++ b/internal/notification/types/init_code.go @@ -9,8 +9,8 @@ import ( "github.com/zitadel/zitadel/internal/query" ) -func (notify Notify) SendUserInitCode(ctx context.Context, user *query.NotifyUser, code string) error { - url := login.InitUserLink(http_utils.ComposedOrigin(ctx), user.ID, user.PreferredLoginName, code, user.ResourceOwner, user.PasswordSet) +func (notify Notify) SendUserInitCode(ctx context.Context, user *query.NotifyUser, code, authRequestID string) error { + url := login.InitUserLink(http_utils.ComposedOrigin(ctx), user.ID, user.PreferredLoginName, code, user.ResourceOwner, user.PasswordSet, authRequestID) args := make(map[string]interface{}) args["Code"] = code return notify(url, args, domain.InitCodeMessageType, true) diff --git a/internal/notification/types/password_code.go b/internal/notification/types/password_code.go index b1121f9d1..c5616e6e2 100644 --- a/internal/notification/types/password_code.go +++ b/internal/notification/types/password_code.go @@ -10,10 +10,10 @@ import ( "github.com/zitadel/zitadel/internal/query" ) -func (notify Notify) SendPasswordCode(ctx context.Context, user *query.NotifyUser, code, urlTmpl string) error { +func (notify Notify) SendPasswordCode(ctx context.Context, user *query.NotifyUser, code, urlTmpl, authRequestID string) error { var url string if urlTmpl == "" { - url = login.InitPasswordLink(http_utils.ComposedOrigin(ctx), user.ID, code, user.ResourceOwner) + url = login.InitPasswordLink(http_utils.ComposedOrigin(ctx), user.ID, code, user.ResourceOwner, authRequestID) } else { var buf strings.Builder if err := domain.RenderConfirmURLTemplate(&buf, urlTmpl, user.ID, code, user.ResourceOwner); err != nil { diff --git a/internal/repository/user/human.go b/internal/repository/user/human.go index c53b9e03d..ab5b76d8a 100644 --- a/internal/repository/user/human.go +++ b/internal/repository/user/human.go @@ -157,6 +157,8 @@ type HumanRegisteredEvent struct { Secret *crypto.CryptoValue `json:"secret,omitempty"` // legacy EncodedHash string `json:"encodedHash,omitempty"` ChangeRequired bool `json:"changeRequired,omitempty"` + + UserAgentID string `json:"userAgentID,omitempty"` } func (e *HumanRegisteredEvent) Payload() interface{} { @@ -208,6 +210,7 @@ func NewHumanRegisteredEvent( gender domain.Gender, emailAddress domain.EmailAddress, userLoginMustBeDomain bool, + userAgentID string, ) *HumanRegisteredEvent { return &HumanRegisteredEvent{ BaseEvent: *eventstore.NewBaseEventForPush( @@ -224,6 +227,7 @@ func NewHumanRegisteredEvent( Gender: gender, EmailAddress: emailAddress, userLoginMustBeDomain: userLoginMustBeDomain, + UserAgentID: userAgentID, } } @@ -244,6 +248,7 @@ type HumanInitialCodeAddedEvent struct { Code *crypto.CryptoValue `json:"code,omitempty"` Expiry time.Duration `json:"expiry,omitempty"` TriggeredAtOrigin string `json:"triggerOrigin,omitempty"` + AuthRequestID string `json:"authRequestID,omitempty"` } func (e *HumanInitialCodeAddedEvent) Payload() interface{} { @@ -263,6 +268,7 @@ func NewHumanInitialCodeAddedEvent( aggregate *eventstore.Aggregate, code *crypto.CryptoValue, expiry time.Duration, + authRequestID string, ) *HumanInitialCodeAddedEvent { return &HumanInitialCodeAddedEvent{ BaseEvent: *eventstore.NewBaseEventForPush( @@ -273,6 +279,7 @@ func NewHumanInitialCodeAddedEvent( Code: code, Expiry: expiry, TriggeredAtOrigin: http.ComposedOrigin(ctx), + AuthRequestID: authRequestID, } } diff --git a/internal/repository/user/human_email.go b/internal/repository/user/human_email.go index b78017aed..aeb2011c7 100644 --- a/internal/repository/user/human_email.go +++ b/internal/repository/user/human_email.go @@ -126,6 +126,8 @@ type HumanEmailCodeAddedEvent struct { URLTemplate string `json:"url_template,omitempty"` CodeReturned bool `json:"code_returned,omitempty"` TriggeredAtOrigin string `json:"triggerOrigin,omitempty"` + // AuthRequest is only used in V1 Login UI + AuthRequestID string `json:"authRequestID,omitempty"` } func (e *HumanEmailCodeAddedEvent) Payload() interface{} { @@ -145,8 +147,19 @@ func NewHumanEmailCodeAddedEvent( aggregate *eventstore.Aggregate, code *crypto.CryptoValue, expiry time.Duration, + authRequestID string, ) *HumanEmailCodeAddedEvent { - return NewHumanEmailCodeAddedEventV2(ctx, aggregate, code, expiry, "", false) + return &HumanEmailCodeAddedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + aggregate, + HumanEmailCodeAddedType, + ), + Code: code, + Expiry: expiry, + TriggeredAtOrigin: http.ComposedOrigin(ctx), + AuthRequestID: authRequestID, + } } func NewHumanEmailCodeAddedEventV2( diff --git a/internal/repository/user/human_password.go b/internal/repository/user/human_password.go index e468cc832..bf7ba278c 100644 --- a/internal/repository/user/human_password.go +++ b/internal/repository/user/human_password.go @@ -87,6 +87,8 @@ type HumanPasswordCodeAddedEvent struct { URLTemplate string `json:"url_template,omitempty"` CodeReturned bool `json:"code_returned,omitempty"` TriggeredAtOrigin string `json:"triggerOrigin,omitempty"` + // AuthRequest is only used in V1 Login UI + AuthRequestID string `json:"authRequestID,omitempty"` } func (e *HumanPasswordCodeAddedEvent) Payload() interface{} { @@ -107,8 +109,20 @@ func NewHumanPasswordCodeAddedEvent( code *crypto.CryptoValue, expiry time.Duration, notificationType domain.NotificationType, + authRequestID string, ) *HumanPasswordCodeAddedEvent { - return NewHumanPasswordCodeAddedEventV2(ctx, aggregate, code, expiry, notificationType, "", false) + return &HumanPasswordCodeAddedEvent{ + BaseEvent: *eventstore.NewBaseEventForPush( + ctx, + aggregate, + HumanPasswordCodeAddedType, + ), + Code: code, + Expiry: expiry, + NotificationType: notificationType, + TriggeredAtOrigin: http.ComposedOrigin(ctx), + AuthRequestID: authRequestID, + } } func NewHumanPasswordCodeAddedEventV2(