diff --git a/cmd/setup/25.sql b/cmd/setup/25.sql index 8a9ee06e03..ca0390a1a1 100644 --- a/cmd/setup/25.sql +++ b/cmd/setup/25.sql @@ -1,2 +1,2 @@ -ALTER TABLE IF EXISTS projections.users13_notifications ADD COLUMN IF NOT EXISTS verified_email_lower TEXT GENERATED ALWAYS AS (lower(verified_email)) STORED; -CREATE INDEX IF NOT EXISTS users13_notifications_email_search ON projections.users13_notifications (instance_id, verified_email_lower); +ALTER TABLE IF EXISTS projections.users14_notifications ADD COLUMN IF NOT EXISTS verified_email_lower TEXT GENERATED ALWAYS AS (lower(verified_email)) STORED; +CREATE INDEX IF NOT EXISTS users14_notifications_email_search ON projections.users14_notifications (instance_id, verified_email_lower); diff --git a/internal/api/grpc/user/v2/integration_test/user_test.go b/internal/api/grpc/user/v2/integration_test/user_test.go index 1d6d12241a..0ed93fd92e 100644 --- a/internal/api/grpc/user/v2/integration_test/user_test.go +++ b/internal/api/grpc/user/v2/integration_test/user_test.go @@ -3179,3 +3179,75 @@ func TestServer_VerifyInviteCode(t *testing.T) { }) } } + +func TestServer_HumanMFAInitSkipped(t *testing.T) { + type args struct { + ctx context.Context + req *user.HumanMFAInitSkippedRequest + prepare func(request *user.HumanMFAInitSkippedRequest) error + } + tests := []struct { + name string + args args + want *user.HumanMFAInitSkippedResponse + checkState func(t *testing.T, userID string, resp *user.HumanMFAInitSkippedResponse) + wantErr bool + }{ + { + name: "user not existing", + args: args{ + CTX, + &user.HumanMFAInitSkippedRequest{ + UserId: "notexisting", + }, + func(request *user.HumanMFAInitSkippedRequest) error { return nil }, + }, + wantErr: true, + }, + { + name: "ok", + args: args{ + CTX, + &user.HumanMFAInitSkippedRequest{}, + func(request *user.HumanMFAInitSkippedRequest) error { + resp := Instance.CreateHumanUser(CTX) + request.UserId = resp.GetUserId() + return nil + }, + }, + want: &user.HumanMFAInitSkippedResponse{ + Details: &object.Details{ + ChangeDate: timestamppb.Now(), + ResourceOwner: Instance.DefaultOrg.Id, + }, + }, + checkState: func(t *testing.T, userID string, resp *user.HumanMFAInitSkippedResponse) { + state, err := Client.GetUserByID(CTX, &user.GetUserByIDRequest{ + UserId: userID, + }) + require.NoError(t, err) + integration.EqualProto(t, + state.GetUser().GetHuman().GetMfaInitSkipped(), + resp.GetDetails().GetChangeDate(), + ) + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := tt.args.prepare(tt.args.req) + require.NoError(t, err) + + got, err := Client.HumanMFAInitSkipped(tt.args.ctx, tt.args.req) + if tt.wantErr { + require.Error(t, err) + return + } + require.NoError(t, err) + integration.AssertDetails(t, tt.want, got) + if tt.checkState != nil { + tt.checkState(t, tt.args.req.GetUserId(), got) + } + }) + } +} diff --git a/internal/api/grpc/user/v2/query.go b/internal/api/grpc/user/v2/query.go index 4cfbb46a51..aeb17d5dcf 100644 --- a/internal/api/grpc/user/v2/query.go +++ b/internal/api/grpc/user/v2/query.go @@ -82,10 +82,13 @@ func userTypeToPb(userQ *query.User, assetPrefix string) user.UserType { } func humanToPb(userQ *query.Human, assetPrefix, owner string) *user.HumanUser { - var passwordChanged *timestamppb.Timestamp + var passwordChanged, mfaInitSkipped *timestamppb.Timestamp if !userQ.PasswordChanged.IsZero() { passwordChanged = timestamppb.New(userQ.PasswordChanged) } + if !userQ.MFAInitSkipped.IsZero() { + mfaInitSkipped = timestamppb.New(userQ.MFAInitSkipped) + } return &user.HumanUser{ Profile: &user.HumanProfile{ GivenName: userQ.FirstName, @@ -106,6 +109,7 @@ func humanToPb(userQ *query.Human, assetPrefix, owner string) *user.HumanUser { }, PasswordChangeRequired: userQ.PasswordChangeRequired, PasswordChanged: passwordChanged, + MfaInitSkipped: mfaInitSkipped, } } diff --git a/internal/api/grpc/user/v2/user.go b/internal/api/grpc/user/v2/user.go index 9d99f210e5..10b21e8918 100644 --- a/internal/api/grpc/user/v2/user.go +++ b/internal/api/grpc/user/v2/user.go @@ -711,3 +711,13 @@ func createInviteCodeRequestToCommand(req *user.CreateInviteCodeRequest) (*comma return &command.CreateUserInvite{UserID: req.GetUserId()}, nil } } + +func (s *Server) HumanMFAInitSkipped(ctx context.Context, req *user.HumanMFAInitSkippedRequest) (_ *user.HumanMFAInitSkippedResponse, err error) { + details, err := s.command.HumanMFAInitSkippedV2(ctx, req.UserId) + if err != nil { + return nil, err + } + return &user.HumanMFAInitSkippedResponse{ + Details: object.DomainToDetailsPb(details), + }, nil +} diff --git a/internal/command/user_v2_human.go b/internal/command/user_v2_human.go index 6ef61f575c..72f76b6730 100644 --- a/internal/command/user_v2_human.go +++ b/internal/command/user_v2_human.go @@ -491,6 +491,28 @@ func (c *Commands) changeUserPassword(ctx context.Context, cmds []eventstore.Com return cmds, err } +func (c *Commands) HumanMFAInitSkippedV2(ctx context.Context, userID string) (*domain.ObjectDetails, error) { + if userID == "" { + return nil, zerrors.ThrowInvalidArgument(nil, "COMMAND-Wei5kooz1i", "Errors.User.UserIDMissing") + } + + existingHuman, err := c.userStateWriteModel(ctx, userID) + if err != nil { + return nil, err + } + if !isUserStateExists(existingHuman.UserState) { + return nil, zerrors.ThrowNotFound(nil, "COMMAND-auj6jeBei4", "Errors.User.NotFound") + } + if err := c.checkPermissionUpdateUser(ctx, existingHuman.ResourceOwner, existingHuman.AggregateID); err != nil { + return nil, err + } + + if err := c.pushAppendAndReduce(ctx, existingHuman, user.NewHumanMFAInitSkippedEvent(ctx, &existingHuman.Aggregate().Aggregate)); err != nil { + return nil, err + } + return writeModelToObjectDetails(&existingHuman.WriteModel), nil +} + func (c *Commands) userExistsWriteModel(ctx context.Context, userID string) (writeModel *UserV2WriteModel, err error) { ctx, span := tracing.NewSpan(ctx) defer func() { span.EndWithError(err) }() diff --git a/internal/query/authn_key_user.sql b/internal/query/authn_key_user.sql index e8eb4c3a1b..5b7eaca63a 100644 --- a/internal/query/authn_key_user.sql +++ b/internal/query/authn_key_user.sql @@ -1,9 +1,9 @@ select u.id as user_id, u.resource_owner, u.username, m.access_token_type, k.public_key from projections.authn_keys2 k -join projections.users13 u +join projections.users14 u on k.instance_id = u.instance_id and k.identifier = u.id -join projections.users13_machines m +join projections.users14_machines m on u.instance_id = m.instance_id and u.id = m.user_id where k.instance_id = $1 diff --git a/internal/query/iam_member_test.go b/internal/query/iam_member_test.go index 38b9bbc8bc..82cea360c8 100644 --- a/internal/query/iam_member_test.go +++ b/internal/query/iam_member_test.go @@ -22,21 +22,21 @@ var ( ", members.user_id" + ", members.roles" + ", projections.login_names3.login_name" + - ", projections.users13_humans.email" + - ", projections.users13_humans.first_name" + - ", projections.users13_humans.last_name" + - ", projections.users13_humans.display_name" + - ", projections.users13_machines.name" + - ", projections.users13_humans.avatar_key" + - ", projections.users13.type" + + ", projections.users14_humans.email" + + ", projections.users14_humans.first_name" + + ", projections.users14_humans.last_name" + + ", projections.users14_humans.display_name" + + ", projections.users14_machines.name" + + ", projections.users14_humans.avatar_key" + + ", projections.users14.type" + ", COUNT(*) OVER () " + "FROM projections.instance_members4 AS members " + - "LEFT JOIN projections.users13_humans " + - "ON members.user_id = projections.users13_humans.user_id AND members.instance_id = projections.users13_humans.instance_id " + - "LEFT JOIN projections.users13_machines " + - "ON members.user_id = projections.users13_machines.user_id AND members.instance_id = projections.users13_machines.instance_id " + - "LEFT JOIN projections.users13 " + - "ON members.user_id = projections.users13.id AND members.instance_id = projections.users13.instance_id " + + "LEFT JOIN projections.users14_humans " + + "ON members.user_id = projections.users14_humans.user_id AND members.instance_id = projections.users14_humans.instance_id " + + "LEFT JOIN projections.users14_machines " + + "ON members.user_id = projections.users14_machines.user_id AND members.instance_id = projections.users14_machines.instance_id " + + "LEFT JOIN projections.users14 " + + "ON members.user_id = projections.users14.id AND members.instance_id = projections.users14.instance_id " + "LEFT JOIN projections.login_names3 " + "ON members.user_id = projections.login_names3.user_id AND members.instance_id = projections.login_names3.instance_id " + "AS OF SYSTEM TIME '-1 ms' " + diff --git a/internal/query/org_member_test.go b/internal/query/org_member_test.go index d42c9b4317..8433c338ee 100644 --- a/internal/query/org_member_test.go +++ b/internal/query/org_member_test.go @@ -22,24 +22,24 @@ var ( ", members.user_id" + ", members.roles" + ", projections.login_names3.login_name" + - ", projections.users13_humans.email" + - ", projections.users13_humans.first_name" + - ", projections.users13_humans.last_name" + - ", projections.users13_humans.display_name" + - ", projections.users13_machines.name" + - ", projections.users13_humans.avatar_key" + - ", projections.users13.type" + + ", projections.users14_humans.email" + + ", projections.users14_humans.first_name" + + ", projections.users14_humans.last_name" + + ", projections.users14_humans.display_name" + + ", projections.users14_machines.name" + + ", projections.users14_humans.avatar_key" + + ", projections.users14.type" + ", COUNT(*) OVER () " + "FROM projections.org_members4 AS members " + - "LEFT JOIN projections.users13_humans " + - "ON members.user_id = projections.users13_humans.user_id " + - "AND members.instance_id = projections.users13_humans.instance_id " + - "LEFT JOIN projections.users13_machines " + - "ON members.user_id = projections.users13_machines.user_id " + - "AND members.instance_id = projections.users13_machines.instance_id " + - "LEFT JOIN projections.users13 " + - "ON members.user_id = projections.users13.id " + - "AND members.instance_id = projections.users13.instance_id " + + "LEFT JOIN projections.users14_humans " + + "ON members.user_id = projections.users14_humans.user_id " + + "AND members.instance_id = projections.users14_humans.instance_id " + + "LEFT JOIN projections.users14_machines " + + "ON members.user_id = projections.users14_machines.user_id " + + "AND members.instance_id = projections.users14_machines.instance_id " + + "LEFT JOIN projections.users14 " + + "ON members.user_id = projections.users14.id " + + "AND members.instance_id = projections.users14.instance_id " + "LEFT JOIN projections.login_names3 " + "ON members.user_id = projections.login_names3.user_id " + "AND members.instance_id = projections.login_names3.instance_id " + diff --git a/internal/query/project_grant_member_test.go b/internal/query/project_grant_member_test.go index f55841ff76..72eaf76d6e 100644 --- a/internal/query/project_grant_member_test.go +++ b/internal/query/project_grant_member_test.go @@ -22,24 +22,24 @@ var ( ", members.user_id" + ", members.roles" + ", projections.login_names3.login_name" + - ", projections.users13_humans.email" + - ", projections.users13_humans.first_name" + - ", projections.users13_humans.last_name" + - ", projections.users13_humans.display_name" + - ", projections.users13_machines.name" + - ", projections.users13_humans.avatar_key" + - ", projections.users13.type" + + ", projections.users14_humans.email" + + ", projections.users14_humans.first_name" + + ", projections.users14_humans.last_name" + + ", projections.users14_humans.display_name" + + ", projections.users14_machines.name" + + ", projections.users14_humans.avatar_key" + + ", projections.users14.type" + ", COUNT(*) OVER () " + "FROM projections.project_grant_members4 AS members " + - "LEFT JOIN projections.users13_humans " + - "ON members.user_id = projections.users13_humans.user_id " + - "AND members.instance_id = projections.users13_humans.instance_id " + - "LEFT JOIN projections.users13_machines " + - "ON members.user_id = projections.users13_machines.user_id " + - "AND members.instance_id = projections.users13_machines.instance_id " + - "LEFT JOIN projections.users13 " + - "ON members.user_id = projections.users13.id " + - "AND members.instance_id = projections.users13.instance_id " + + "LEFT JOIN projections.users14_humans " + + "ON members.user_id = projections.users14_humans.user_id " + + "AND members.instance_id = projections.users14_humans.instance_id " + + "LEFT JOIN projections.users14_machines " + + "ON members.user_id = projections.users14_machines.user_id " + + "AND members.instance_id = projections.users14_machines.instance_id " + + "LEFT JOIN projections.users14 " + + "ON members.user_id = projections.users14.id " + + "AND members.instance_id = projections.users14.instance_id " + "LEFT JOIN projections.login_names3 " + "ON members.user_id = projections.login_names3.user_id " + "AND members.instance_id = projections.login_names3.instance_id " + diff --git a/internal/query/project_member_test.go b/internal/query/project_member_test.go index 21be454f43..24548dadfb 100644 --- a/internal/query/project_member_test.go +++ b/internal/query/project_member_test.go @@ -22,24 +22,24 @@ var ( ", members.user_id" + ", members.roles" + ", projections.login_names3.login_name" + - ", projections.users13_humans.email" + - ", projections.users13_humans.first_name" + - ", projections.users13_humans.last_name" + - ", projections.users13_humans.display_name" + - ", projections.users13_machines.name" + - ", projections.users13_humans.avatar_key" + - ", projections.users13.type" + + ", projections.users14_humans.email" + + ", projections.users14_humans.first_name" + + ", projections.users14_humans.last_name" + + ", projections.users14_humans.display_name" + + ", projections.users14_machines.name" + + ", projections.users14_humans.avatar_key" + + ", projections.users14.type" + ", COUNT(*) OVER () " + "FROM projections.project_members4 AS members " + - "LEFT JOIN projections.users13_humans " + - "ON members.user_id = projections.users13_humans.user_id " + - "AND members.instance_id = projections.users13_humans.instance_id " + - "LEFT JOIN projections.users13_machines " + - "ON members.user_id = projections.users13_machines.user_id " + - "AND members.instance_id = projections.users13_machines.instance_id " + - "LEFT JOIN projections.users13 " + - "ON members.user_id = projections.users13.id " + - "AND members.instance_id = projections.users13.instance_id " + + "LEFT JOIN projections.users14_humans " + + "ON members.user_id = projections.users14_humans.user_id " + + "AND members.instance_id = projections.users14_humans.instance_id " + + "LEFT JOIN projections.users14_machines " + + "ON members.user_id = projections.users14_machines.user_id " + + "AND members.instance_id = projections.users14_machines.instance_id " + + "LEFT JOIN projections.users14 " + + "ON members.user_id = projections.users14.id " + + "AND members.instance_id = projections.users14.instance_id " + "LEFT JOIN projections.login_names3 " + "ON members.user_id = projections.login_names3.user_id " + "AND members.instance_id = projections.login_names3.instance_id " + diff --git a/internal/query/projection/user.go b/internal/query/projection/user.go index f7c2981e4c..f1e0613287 100644 --- a/internal/query/projection/user.go +++ b/internal/query/projection/user.go @@ -16,7 +16,7 @@ import ( ) const ( - UserTable = "projections.users13" + UserTable = "projections.users14" UserHumanTable = UserTable + "_" + UserHumanSuffix UserMachineTable = UserTable + "_" + UserMachineSuffix UserNotifyTable = UserTable + "_" + UserNotifySuffix @@ -36,6 +36,7 @@ const ( HumanUserInstanceIDCol = "instance_id" HumanPasswordChangeRequired = "password_change_required" HumanPasswordChanged = "password_changed" + HumanMFAInitSkipped = "mfa_init_skipped" // profile HumanFirstNameCol = "first_name" @@ -118,6 +119,7 @@ func (*userProjection) Init() *old_handler.Check { handler.NewColumn(HumanIsPhoneVerifiedCol, handler.ColumnTypeBool, handler.Nullable()), handler.NewColumn(HumanPasswordChangeRequired, handler.ColumnTypeBool), handler.NewColumn(HumanPasswordChanged, handler.ColumnTypeTimestamp, handler.Nullable()), + handler.NewColumn(HumanMFAInitSkipped, handler.ColumnTypeTimestamp, handler.Nullable()), }, handler.NewPrimaryKey(HumanUserInstanceIDCol, HumanUserIDCol), UserHumanSuffix, @@ -296,6 +298,38 @@ func (p *userProjection) Reducers() []handler.AggregateReducer { Event: user.MachineSecretRemovedType, Reduce: p.reduceMachineSecretRemoved, }, + { + Event: user.UserV1MFAOTPVerifiedType, + Reduce: p.reduceUnsetMFAInitSkipped, + }, + { + Event: user.HumanMFAOTPVerifiedType, + Reduce: p.reduceUnsetMFAInitSkipped, + }, + { + Event: user.HumanOTPSMSAddedType, + Reduce: p.reduceUnsetMFAInitSkipped, + }, + { + Event: user.HumanOTPEmailAddedType, + Reduce: p.reduceUnsetMFAInitSkipped, + }, + { + Event: user.HumanU2FTokenVerifiedType, + Reduce: p.reduceUnsetMFAInitSkipped, + }, + { + Event: user.HumanPasswordlessTokenVerifiedType, + Reduce: p.reduceUnsetMFAInitSkipped, + }, + { + Event: user.UserV1MFAInitSkippedType, + Reduce: p.reduceMFAInitSkipped, + }, + { + Event: user.HumanMFAInitSkippedType, + Reduce: p.reduceMFAInitSkipped, + }, }, }, { @@ -1114,6 +1148,51 @@ func (p *userProjection) reduceMachineChanged(event eventstore.Event) (*handler. } +func (p *userProjection) reduceUnsetMFAInitSkipped(e eventstore.Event) (*handler.Statement, error) { + switch e.(type) { + default: + return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-ojrf6", "reduce.wrong.event.type %s", e.Type()) + case *user.HumanOTPVerifiedEvent, + *user.HumanOTPSMSAddedEvent, + *user.HumanOTPEmailAddedEvent, + *user.HumanU2FVerifiedEvent, + *user.HumanPasswordlessVerifiedEvent: + } + + return handler.NewUpdateStatement( + e, + []handler.Column{ + handler.NewCol(HumanMFAInitSkipped, sql.NullTime{}), + }, + []handler.Condition{ + handler.NewCond(HumanUserIDCol, e.Aggregate().ID), + handler.NewCond(HumanUserInstanceIDCol, e.Aggregate().InstanceID), + }, + handler.WithTableSuffix(UserHumanSuffix), + ), nil +} + +func (p *userProjection) reduceMFAInitSkipped(event eventstore.Event) (*handler.Statement, error) { + e, ok := event.(*user.HumanMFAInitSkippedEvent) + if !ok { + return nil, zerrors.ThrowInvalidArgumentf(nil, "HANDL-qYHvj", "reduce.wrong.event.type %s", user.MachineChangedEventType) + } + return handler.NewUpdateStatement( + e, + []handler.Column{ + handler.NewCol(HumanMFAInitSkipped, sql.NullTime{ + Time: e.CreatedAt(), + Valid: true, + }), + }, + []handler.Condition{ + handler.NewCond(HumanUserIDCol, e.Aggregate().ID), + handler.NewCond(HumanUserInstanceIDCol, e.Aggregate().InstanceID), + }, + handler.WithTableSuffix(UserHumanSuffix), + ), nil +} + func (p *userProjection) reduceOwnerRemoved(event eventstore.Event) (*handler.Statement, error) { e, ok := event.(*org.OrgRemovedEvent) if !ok { diff --git a/internal/query/projection/user_test.go b/internal/query/projection/user_test.go index 61091470b0..307a5bfe14 100644 --- a/internal/query/projection/user_test.go +++ b/internal/query/projection/user_test.go @@ -54,7 +54,7 @@ func TestUserProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "INSERT INTO projections.users13 (id, creation_date, change_date, resource_owner, instance_id, state, sequence, username, type) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)", + expectedStmt: "INSERT INTO projections.users14 (id, creation_date, change_date, resource_owner, instance_id, state, sequence, username, type) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)", expectedArgs: []interface{}{ "agg-id", anyArg{}, @@ -68,7 +68,7 @@ func TestUserProjection_reduces(t *testing.T) { }, }, { - expectedStmt: "INSERT INTO projections.users13_humans (user_id, instance_id, first_name, last_name, nick_name, display_name, preferred_language, gender, email, phone, password_change_required, password_changed) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)", + expectedStmt: "INSERT INTO projections.users14_humans (user_id, instance_id, first_name, last_name, nick_name, display_name, preferred_language, gender, email, phone, password_change_required, password_changed) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)", expectedArgs: []interface{}{ "agg-id", "instance-id", @@ -85,7 +85,7 @@ func TestUserProjection_reduces(t *testing.T) { }, }, { - expectedStmt: "INSERT INTO projections.users13_notifications (user_id, instance_id, last_email, last_phone, password_set) VALUES ($1, $2, $3, $4, $5)", + expectedStmt: "INSERT INTO projections.users14_notifications (user_id, instance_id, last_email, last_phone, password_set) VALUES ($1, $2, $3, $4, $5)", expectedArgs: []interface{}{ "agg-id", "instance-id", @@ -126,7 +126,7 @@ func TestUserProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "INSERT INTO projections.users13 (id, creation_date, change_date, resource_owner, instance_id, state, sequence, username, type) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)", + expectedStmt: "INSERT INTO projections.users14 (id, creation_date, change_date, resource_owner, instance_id, state, sequence, username, type) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)", expectedArgs: []interface{}{ "agg-id", anyArg{}, @@ -140,7 +140,7 @@ func TestUserProjection_reduces(t *testing.T) { }, }, { - expectedStmt: "INSERT INTO projections.users13_humans (user_id, instance_id, first_name, last_name, nick_name, display_name, preferred_language, gender, email, phone, password_change_required, password_changed) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)", + expectedStmt: "INSERT INTO projections.users14_humans (user_id, instance_id, first_name, last_name, nick_name, display_name, preferred_language, gender, email, phone, password_change_required, password_changed) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)", expectedArgs: []interface{}{ "agg-id", "instance-id", @@ -157,7 +157,7 @@ func TestUserProjection_reduces(t *testing.T) { }, }, { - expectedStmt: "INSERT INTO projections.users13_notifications (user_id, instance_id, last_email, last_phone, password_set) VALUES ($1, $2, $3, $4, $5)", + expectedStmt: "INSERT INTO projections.users14_notifications (user_id, instance_id, last_email, last_phone, password_set) VALUES ($1, $2, $3, $4, $5)", expectedArgs: []interface{}{ "agg-id", "instance-id", @@ -193,7 +193,7 @@ func TestUserProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "INSERT INTO projections.users13 (id, creation_date, change_date, resource_owner, instance_id, state, sequence, username, type) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)", + expectedStmt: "INSERT INTO projections.users14 (id, creation_date, change_date, resource_owner, instance_id, state, sequence, username, type) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)", expectedArgs: []interface{}{ "agg-id", anyArg{}, @@ -207,7 +207,7 @@ func TestUserProjection_reduces(t *testing.T) { }, }, { - expectedStmt: "INSERT INTO projections.users13_humans (user_id, instance_id, first_name, last_name, nick_name, display_name, preferred_language, gender, email, phone, password_change_required, password_changed) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)", + expectedStmt: "INSERT INTO projections.users14_humans (user_id, instance_id, first_name, last_name, nick_name, display_name, preferred_language, gender, email, phone, password_change_required, password_changed) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)", expectedArgs: []interface{}{ "agg-id", "instance-id", @@ -224,7 +224,7 @@ func TestUserProjection_reduces(t *testing.T) { }, }, { - expectedStmt: "INSERT INTO projections.users13_notifications (user_id, instance_id, last_email, last_phone, password_set) VALUES ($1, $2, $3, $4, $5)", + expectedStmt: "INSERT INTO projections.users14_notifications (user_id, instance_id, last_email, last_phone, password_set) VALUES ($1, $2, $3, $4, $5)", expectedArgs: []interface{}{ "agg-id", "instance-id", @@ -266,7 +266,7 @@ func TestUserProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "INSERT INTO projections.users13 (id, creation_date, change_date, resource_owner, instance_id, state, sequence, username, type) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)", + expectedStmt: "INSERT INTO projections.users14 (id, creation_date, change_date, resource_owner, instance_id, state, sequence, username, type) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)", expectedArgs: []interface{}{ "agg-id", anyArg{}, @@ -280,7 +280,7 @@ func TestUserProjection_reduces(t *testing.T) { }, }, { - expectedStmt: "INSERT INTO projections.users13_humans (user_id, instance_id, first_name, last_name, nick_name, display_name, preferred_language, gender, email, phone, password_change_required, password_changed) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)", + expectedStmt: "INSERT INTO projections.users14_humans (user_id, instance_id, first_name, last_name, nick_name, display_name, preferred_language, gender, email, phone, password_change_required, password_changed) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)", expectedArgs: []interface{}{ "agg-id", "instance-id", @@ -297,7 +297,7 @@ func TestUserProjection_reduces(t *testing.T) { }, }, { - expectedStmt: "INSERT INTO projections.users13_notifications (user_id, instance_id, last_email, last_phone, password_set) VALUES ($1, $2, $3, $4, $5)", + expectedStmt: "INSERT INTO projections.users14_notifications (user_id, instance_id, last_email, last_phone, password_set) VALUES ($1, $2, $3, $4, $5)", expectedArgs: []interface{}{ "agg-id", "instance-id", @@ -338,7 +338,7 @@ func TestUserProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "INSERT INTO projections.users13 (id, creation_date, change_date, resource_owner, instance_id, state, sequence, username, type) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)", + expectedStmt: "INSERT INTO projections.users14 (id, creation_date, change_date, resource_owner, instance_id, state, sequence, username, type) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)", expectedArgs: []interface{}{ "agg-id", anyArg{}, @@ -352,7 +352,7 @@ func TestUserProjection_reduces(t *testing.T) { }, }, { - expectedStmt: "INSERT INTO projections.users13_humans (user_id, instance_id, first_name, last_name, nick_name, display_name, preferred_language, gender, email, phone, password_change_required, password_changed) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)", + expectedStmt: "INSERT INTO projections.users14_humans (user_id, instance_id, first_name, last_name, nick_name, display_name, preferred_language, gender, email, phone, password_change_required, password_changed) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)", expectedArgs: []interface{}{ "agg-id", "instance-id", @@ -369,7 +369,7 @@ func TestUserProjection_reduces(t *testing.T) { }, }, { - expectedStmt: "INSERT INTO projections.users13_notifications (user_id, instance_id, last_email, last_phone, password_set) VALUES ($1, $2, $3, $4, $5)", + expectedStmt: "INSERT INTO projections.users14_notifications (user_id, instance_id, last_email, last_phone, password_set) VALUES ($1, $2, $3, $4, $5)", expectedArgs: []interface{}{ "agg-id", "instance-id", @@ -405,7 +405,7 @@ func TestUserProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "INSERT INTO projections.users13 (id, creation_date, change_date, resource_owner, instance_id, state, sequence, username, type) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)", + expectedStmt: "INSERT INTO projections.users14 (id, creation_date, change_date, resource_owner, instance_id, state, sequence, username, type) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)", expectedArgs: []interface{}{ "agg-id", anyArg{}, @@ -419,7 +419,7 @@ func TestUserProjection_reduces(t *testing.T) { }, }, { - expectedStmt: "INSERT INTO projections.users13_humans (user_id, instance_id, first_name, last_name, nick_name, display_name, preferred_language, gender, email, phone, password_change_required, password_changed) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)", + expectedStmt: "INSERT INTO projections.users14_humans (user_id, instance_id, first_name, last_name, nick_name, display_name, preferred_language, gender, email, phone, password_change_required, password_changed) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)", expectedArgs: []interface{}{ "agg-id", "instance-id", @@ -436,7 +436,7 @@ func TestUserProjection_reduces(t *testing.T) { }, }, { - expectedStmt: "INSERT INTO projections.users13_notifications (user_id, instance_id, last_email, last_phone, password_set) VALUES ($1, $2, $3, $4, $5)", + expectedStmt: "INSERT INTO projections.users14_notifications (user_id, instance_id, last_email, last_phone, password_set) VALUES ($1, $2, $3, $4, $5)", expectedArgs: []interface{}{ "agg-id", "instance-id", @@ -466,7 +466,7 @@ func TestUserProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.users13 SET state = $1 WHERE (id = $2) AND (instance_id = $3)", + expectedStmt: "UPDATE projections.users14 SET state = $1 WHERE (id = $2) AND (instance_id = $3)", expectedArgs: []interface{}{ domain.UserStateInitial, "agg-id", @@ -494,7 +494,7 @@ func TestUserProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.users13 SET state = $1 WHERE (id = $2) AND (instance_id = $3)", + expectedStmt: "UPDATE projections.users14 SET state = $1 WHERE (id = $2) AND (instance_id = $3)", expectedArgs: []interface{}{ domain.UserStateInitial, "agg-id", @@ -522,7 +522,7 @@ func TestUserProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.users13 SET state = $1 WHERE (id = $2) AND (instance_id = $3)", + expectedStmt: "UPDATE projections.users14 SET state = $1 WHERE (id = $2) AND (instance_id = $3)", expectedArgs: []interface{}{ domain.UserStateActive, "agg-id", @@ -550,7 +550,7 @@ func TestUserProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.users13 SET state = $1 WHERE (id = $2) AND (instance_id = $3)", + expectedStmt: "UPDATE projections.users14 SET state = $1 WHERE (id = $2) AND (instance_id = $3)", expectedArgs: []interface{}{ domain.UserStateActive, "agg-id", @@ -578,7 +578,7 @@ func TestUserProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.users13 SET (change_date, state, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", + expectedStmt: "UPDATE projections.users14 SET (change_date, state, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", expectedArgs: []interface{}{ anyArg{}, domain.UserStateLocked, @@ -608,7 +608,7 @@ func TestUserProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.users13 SET (change_date, state, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", + expectedStmt: "UPDATE projections.users14 SET (change_date, state, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", expectedArgs: []interface{}{ anyArg{}, domain.UserStateActive, @@ -638,7 +638,7 @@ func TestUserProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.users13 SET (change_date, state, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", + expectedStmt: "UPDATE projections.users14 SET (change_date, state, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", expectedArgs: []interface{}{ anyArg{}, domain.UserStateInactive, @@ -668,7 +668,7 @@ func TestUserProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.users13 SET (change_date, state, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", + expectedStmt: "UPDATE projections.users14 SET (change_date, state, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", expectedArgs: []interface{}{ anyArg{}, domain.UserStateActive, @@ -698,7 +698,7 @@ func TestUserProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "DELETE FROM projections.users13 WHERE (id = $1) AND (instance_id = $2)", + expectedStmt: "DELETE FROM projections.users14 WHERE (id = $1) AND (instance_id = $2)", expectedArgs: []interface{}{ "agg-id", "instance-id", @@ -727,7 +727,7 @@ func TestUserProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.users13 SET (change_date, username, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", + expectedStmt: "UPDATE projections.users14 SET (change_date, username, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", expectedArgs: []interface{}{ anyArg{}, "username", @@ -759,7 +759,7 @@ func TestUserProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.users13 SET (change_date, username, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", + expectedStmt: "UPDATE projections.users14 SET (change_date, username, sequence) = ($1, $2, $3) WHERE (id = $4) AND (instance_id = $5)", expectedArgs: []interface{}{ anyArg{}, "id@temporary.domain", @@ -796,7 +796,7 @@ func TestUserProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.users13 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", + expectedStmt: "UPDATE projections.users14 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ anyArg{}, uint64(15), @@ -805,7 +805,7 @@ func TestUserProjection_reduces(t *testing.T) { }, }, { - expectedStmt: "UPDATE projections.users13_humans SET (first_name, last_name, nick_name, display_name, preferred_language, gender) = ($1, $2, $3, $4, $5, $6) WHERE (user_id = $7) AND (instance_id = $8)", + expectedStmt: "UPDATE projections.users14_humans SET (first_name, last_name, nick_name, display_name, preferred_language, gender) = ($1, $2, $3, $4, $5, $6) WHERE (user_id = $7) AND (instance_id = $8)", expectedArgs: []interface{}{ "first-name", "last-name", @@ -845,7 +845,7 @@ func TestUserProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.users13 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", + expectedStmt: "UPDATE projections.users14 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ anyArg{}, uint64(15), @@ -854,7 +854,7 @@ func TestUserProjection_reduces(t *testing.T) { }, }, { - expectedStmt: "UPDATE projections.users13_humans SET (first_name, last_name, nick_name, display_name, preferred_language, gender) = ($1, $2, $3, $4, $5, $6) WHERE (user_id = $7) AND (instance_id = $8)", + expectedStmt: "UPDATE projections.users14_humans SET (first_name, last_name, nick_name, display_name, preferred_language, gender) = ($1, $2, $3, $4, $5, $6) WHERE (user_id = $7) AND (instance_id = $8)", expectedArgs: []interface{}{ "first-name", "last-name", @@ -889,7 +889,7 @@ func TestUserProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.users13 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", + expectedStmt: "UPDATE projections.users14 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ anyArg{}, uint64(15), @@ -898,7 +898,7 @@ func TestUserProjection_reduces(t *testing.T) { }, }, { - expectedStmt: "UPDATE projections.users13_humans SET (phone, is_phone_verified) = ($1, $2) WHERE (user_id = $3) AND (instance_id = $4)", + expectedStmt: "UPDATE projections.users14_humans SET (phone, is_phone_verified) = ($1, $2) WHERE (user_id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ domain.PhoneNumber("+41 00 000 00 00"), false, @@ -907,7 +907,7 @@ func TestUserProjection_reduces(t *testing.T) { }, }, { - expectedStmt: "UPDATE projections.users13_notifications SET last_phone = $1 WHERE (user_id = $2) AND (instance_id = $3)", + expectedStmt: "UPDATE projections.users14_notifications SET last_phone = $1 WHERE (user_id = $2) AND (instance_id = $3)", expectedArgs: []interface{}{ &sql.NullString{String: "+41 00 000 00 00", Valid: true}, "agg-id", @@ -937,7 +937,7 @@ func TestUserProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.users13 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", + expectedStmt: "UPDATE projections.users14 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ anyArg{}, uint64(15), @@ -946,7 +946,7 @@ func TestUserProjection_reduces(t *testing.T) { }, }, { - expectedStmt: "UPDATE projections.users13_humans SET (phone, is_phone_verified) = ($1, $2) WHERE (user_id = $3) AND (instance_id = $4)", + expectedStmt: "UPDATE projections.users14_humans SET (phone, is_phone_verified) = ($1, $2) WHERE (user_id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ domain.PhoneNumber("+41 00 000 00 00"), false, @@ -955,7 +955,7 @@ func TestUserProjection_reduces(t *testing.T) { }, }, { - expectedStmt: "UPDATE projections.users13_notifications SET last_phone = $1 WHERE (user_id = $2) AND (instance_id = $3)", + expectedStmt: "UPDATE projections.users14_notifications SET last_phone = $1 WHERE (user_id = $2) AND (instance_id = $3)", expectedArgs: []interface{}{ &sql.NullString{String: "+41 00 000 00 00", Valid: true}, "agg-id", @@ -983,7 +983,7 @@ func TestUserProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.users13 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", + expectedStmt: "UPDATE projections.users14 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ anyArg{}, uint64(15), @@ -992,7 +992,7 @@ func TestUserProjection_reduces(t *testing.T) { }, }, { - expectedStmt: "UPDATE projections.users13_humans SET (phone, is_phone_verified) = ($1, $2) WHERE (user_id = $3) AND (instance_id = $4)", + expectedStmt: "UPDATE projections.users14_humans SET (phone, is_phone_verified) = ($1, $2) WHERE (user_id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ nil, nil, @@ -1001,7 +1001,7 @@ func TestUserProjection_reduces(t *testing.T) { }, }, { - expectedStmt: "UPDATE projections.users13_notifications SET (last_phone, verified_phone) = ($1, $2) WHERE (user_id = $3) AND (instance_id = $4)", + expectedStmt: "UPDATE projections.users14_notifications SET (last_phone, verified_phone) = ($1, $2) WHERE (user_id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ nil, nil, @@ -1030,7 +1030,7 @@ func TestUserProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.users13 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", + expectedStmt: "UPDATE projections.users14 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ anyArg{}, uint64(15), @@ -1039,7 +1039,7 @@ func TestUserProjection_reduces(t *testing.T) { }, }, { - expectedStmt: "UPDATE projections.users13_humans SET (phone, is_phone_verified) = ($1, $2) WHERE (user_id = $3) AND (instance_id = $4)", + expectedStmt: "UPDATE projections.users14_humans SET (phone, is_phone_verified) = ($1, $2) WHERE (user_id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ nil, nil, @@ -1048,7 +1048,7 @@ func TestUserProjection_reduces(t *testing.T) { }, }, { - expectedStmt: "UPDATE projections.users13_notifications SET (last_phone, verified_phone) = ($1, $2) WHERE (user_id = $3) AND (instance_id = $4)", + expectedStmt: "UPDATE projections.users14_notifications SET (last_phone, verified_phone) = ($1, $2) WHERE (user_id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ nil, nil, @@ -1077,7 +1077,7 @@ func TestUserProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.users13 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", + expectedStmt: "UPDATE projections.users14 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ anyArg{}, uint64(15), @@ -1086,7 +1086,7 @@ func TestUserProjection_reduces(t *testing.T) { }, }, { - expectedStmt: "UPDATE projections.users13_humans SET is_phone_verified = $1 WHERE (user_id = $2) AND (instance_id = $3)", + expectedStmt: "UPDATE projections.users14_humans SET is_phone_verified = $1 WHERE (user_id = $2) AND (instance_id = $3)", expectedArgs: []interface{}{ true, "agg-id", @@ -1094,7 +1094,7 @@ func TestUserProjection_reduces(t *testing.T) { }, }, { - expectedStmt: "UPDATE projections.users13_notifications SET verified_phone = last_phone WHERE (user_id = $1) AND (instance_id = $2)", + expectedStmt: "UPDATE projections.users14_notifications SET verified_phone = last_phone WHERE (user_id = $1) AND (instance_id = $2)", expectedArgs: []interface{}{ "agg-id", "instance-id", @@ -1121,7 +1121,7 @@ func TestUserProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.users13 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", + expectedStmt: "UPDATE projections.users14 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ anyArg{}, uint64(15), @@ -1130,7 +1130,7 @@ func TestUserProjection_reduces(t *testing.T) { }, }, { - expectedStmt: "UPDATE projections.users13_humans SET is_phone_verified = $1 WHERE (user_id = $2) AND (instance_id = $3)", + expectedStmt: "UPDATE projections.users14_humans SET is_phone_verified = $1 WHERE (user_id = $2) AND (instance_id = $3)", expectedArgs: []interface{}{ true, "agg-id", @@ -1138,7 +1138,7 @@ func TestUserProjection_reduces(t *testing.T) { }, }, { - expectedStmt: "UPDATE projections.users13_notifications SET verified_phone = last_phone WHERE (user_id = $1) AND (instance_id = $2)", + expectedStmt: "UPDATE projections.users14_notifications SET verified_phone = last_phone WHERE (user_id = $1) AND (instance_id = $2)", expectedArgs: []interface{}{ "agg-id", "instance-id", @@ -1167,7 +1167,7 @@ func TestUserProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.users13 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", + expectedStmt: "UPDATE projections.users14 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ anyArg{}, uint64(15), @@ -1176,7 +1176,7 @@ func TestUserProjection_reduces(t *testing.T) { }, }, { - expectedStmt: "UPDATE projections.users13_humans SET (email, is_email_verified) = ($1, $2) WHERE (user_id = $3) AND (instance_id = $4)", + expectedStmt: "UPDATE projections.users14_humans SET (email, is_email_verified) = ($1, $2) WHERE (user_id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ domain.EmailAddress("email@zitadel.com"), false, @@ -1185,7 +1185,7 @@ func TestUserProjection_reduces(t *testing.T) { }, }, { - expectedStmt: "UPDATE projections.users13_notifications SET last_email = $1 WHERE (user_id = $2) AND (instance_id = $3)", + expectedStmt: "UPDATE projections.users14_notifications SET last_email = $1 WHERE (user_id = $2) AND (instance_id = $3)", expectedArgs: []interface{}{ &sql.NullString{String: "email@zitadel.com", Valid: true}, "agg-id", @@ -1215,7 +1215,7 @@ func TestUserProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.users13 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", + expectedStmt: "UPDATE projections.users14 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ anyArg{}, uint64(15), @@ -1224,7 +1224,7 @@ func TestUserProjection_reduces(t *testing.T) { }, }, { - expectedStmt: "UPDATE projections.users13_humans SET (email, is_email_verified) = ($1, $2) WHERE (user_id = $3) AND (instance_id = $4)", + expectedStmt: "UPDATE projections.users14_humans SET (email, is_email_verified) = ($1, $2) WHERE (user_id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ domain.EmailAddress("email@zitadel.com"), false, @@ -1233,7 +1233,7 @@ func TestUserProjection_reduces(t *testing.T) { }, }, { - expectedStmt: "UPDATE projections.users13_notifications SET last_email = $1 WHERE (user_id = $2) AND (instance_id = $3)", + expectedStmt: "UPDATE projections.users14_notifications SET last_email = $1 WHERE (user_id = $2) AND (instance_id = $3)", expectedArgs: []interface{}{ &sql.NullString{String: "email@zitadel.com", Valid: true}, "agg-id", @@ -1261,7 +1261,7 @@ func TestUserProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.users13 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", + expectedStmt: "UPDATE projections.users14 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ anyArg{}, uint64(15), @@ -1270,7 +1270,7 @@ func TestUserProjection_reduces(t *testing.T) { }, }, { - expectedStmt: "UPDATE projections.users13_humans SET is_email_verified = $1 WHERE (user_id = $2) AND (instance_id = $3)", + expectedStmt: "UPDATE projections.users14_humans SET is_email_verified = $1 WHERE (user_id = $2) AND (instance_id = $3)", expectedArgs: []interface{}{ true, "agg-id", @@ -1278,7 +1278,7 @@ func TestUserProjection_reduces(t *testing.T) { }, }, { - expectedStmt: "UPDATE projections.users13_notifications SET verified_email = last_email WHERE (user_id = $1) AND (instance_id = $2)", + expectedStmt: "UPDATE projections.users14_notifications SET verified_email = last_email WHERE (user_id = $1) AND (instance_id = $2)", expectedArgs: []interface{}{ "agg-id", "instance-id", @@ -1305,7 +1305,7 @@ func TestUserProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.users13 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", + expectedStmt: "UPDATE projections.users14 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ anyArg{}, uint64(15), @@ -1314,7 +1314,7 @@ func TestUserProjection_reduces(t *testing.T) { }, }, { - expectedStmt: "UPDATE projections.users13_humans SET is_email_verified = $1 WHERE (user_id = $2) AND (instance_id = $3)", + expectedStmt: "UPDATE projections.users14_humans SET is_email_verified = $1 WHERE (user_id = $2) AND (instance_id = $3)", expectedArgs: []interface{}{ true, "agg-id", @@ -1322,7 +1322,7 @@ func TestUserProjection_reduces(t *testing.T) { }, }, { - expectedStmt: "UPDATE projections.users13_notifications SET verified_email = last_email WHERE (user_id = $1) AND (instance_id = $2)", + expectedStmt: "UPDATE projections.users14_notifications SET verified_email = last_email WHERE (user_id = $1) AND (instance_id = $2)", expectedArgs: []interface{}{ "agg-id", "instance-id", @@ -1351,7 +1351,7 @@ func TestUserProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.users13 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", + expectedStmt: "UPDATE projections.users14 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ anyArg{}, uint64(15), @@ -1360,7 +1360,7 @@ func TestUserProjection_reduces(t *testing.T) { }, }, { - expectedStmt: "UPDATE projections.users13_humans SET avatar_key = $1 WHERE (user_id = $2) AND (instance_id = $3)", + expectedStmt: "UPDATE projections.users14_humans SET avatar_key = $1 WHERE (user_id = $2) AND (instance_id = $3)", expectedArgs: []interface{}{ "users/agg-id/avatar", "agg-id", @@ -1388,7 +1388,7 @@ func TestUserProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.users13 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", + expectedStmt: "UPDATE projections.users14 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ anyArg{}, uint64(15), @@ -1397,7 +1397,7 @@ func TestUserProjection_reduces(t *testing.T) { }, }, { - expectedStmt: "UPDATE projections.users13_humans SET avatar_key = $1 WHERE (user_id = $2) AND (instance_id = $3)", + expectedStmt: "UPDATE projections.users14_humans SET avatar_key = $1 WHERE (user_id = $2) AND (instance_id = $3)", expectedArgs: []interface{}{ nil, "agg-id", @@ -1428,7 +1428,7 @@ func TestUserProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.users13_humans SET (password_change_required, password_changed) = ($1, $2) WHERE (user_id = $3) AND (instance_id = $4)", + expectedStmt: "UPDATE projections.users14_humans SET (password_change_required, password_changed) = ($1, $2) WHERE (user_id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ true, &sql.NullTime{Time: testNow, Valid: true}, @@ -1437,7 +1437,7 @@ func TestUserProjection_reduces(t *testing.T) { }, }, { - expectedStmt: "UPDATE projections.users13_notifications SET password_set = $1 WHERE (user_id = $2) AND (instance_id = $3)", + expectedStmt: "UPDATE projections.users14_notifications SET password_set = $1 WHERE (user_id = $2) AND (instance_id = $3)", expectedArgs: []interface{}{ true, "agg-id", @@ -1468,7 +1468,7 @@ func TestUserProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.users13_humans SET (password_change_required, password_changed) = ($1, $2) WHERE (user_id = $3) AND (instance_id = $4)", + expectedStmt: "UPDATE projections.users14_humans SET (password_change_required, password_changed) = ($1, $2) WHERE (user_id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ false, &sql.NullTime{Time: testNow, Valid: true}, @@ -1477,7 +1477,7 @@ func TestUserProjection_reduces(t *testing.T) { }, }, { - expectedStmt: "UPDATE projections.users13_notifications SET password_set = $1 WHERE (user_id = $2) AND (instance_id = $3)", + expectedStmt: "UPDATE projections.users14_notifications SET password_set = $1 WHERE (user_id = $2) AND (instance_id = $3)", expectedArgs: []interface{}{ true, "agg-id", @@ -1508,7 +1508,7 @@ func TestUserProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "INSERT INTO projections.users13 (id, creation_date, change_date, resource_owner, instance_id, state, sequence, username, type) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)", + expectedStmt: "INSERT INTO projections.users14 (id, creation_date, change_date, resource_owner, instance_id, state, sequence, username, type) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)", expectedArgs: []interface{}{ "agg-id", anyArg{}, @@ -1522,7 +1522,7 @@ func TestUserProjection_reduces(t *testing.T) { }, }, { - expectedStmt: "INSERT INTO projections.users13_machines (user_id, instance_id, name, description, access_token_type) VALUES ($1, $2, $3, $4, $5)", + expectedStmt: "INSERT INTO projections.users14_machines (user_id, instance_id, name, description, access_token_type) VALUES ($1, $2, $3, $4, $5)", expectedArgs: []interface{}{ "agg-id", "instance-id", @@ -1556,7 +1556,7 @@ func TestUserProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "INSERT INTO projections.users13 (id, creation_date, change_date, resource_owner, instance_id, state, sequence, username, type) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)", + expectedStmt: "INSERT INTO projections.users14 (id, creation_date, change_date, resource_owner, instance_id, state, sequence, username, type) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)", expectedArgs: []interface{}{ "agg-id", anyArg{}, @@ -1570,7 +1570,7 @@ func TestUserProjection_reduces(t *testing.T) { }, }, { - expectedStmt: "INSERT INTO projections.users13_machines (user_id, instance_id, name, description, access_token_type) VALUES ($1, $2, $3, $4, $5)", + expectedStmt: "INSERT INTO projections.users14_machines (user_id, instance_id, name, description, access_token_type) VALUES ($1, $2, $3, $4, $5)", expectedArgs: []interface{}{ "agg-id", "instance-id", @@ -1603,7 +1603,7 @@ func TestUserProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.users13 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", + expectedStmt: "UPDATE projections.users14 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ anyArg{}, uint64(15), @@ -1612,7 +1612,7 @@ func TestUserProjection_reduces(t *testing.T) { }, }, { - expectedStmt: "UPDATE projections.users13_machines SET (name, description) = ($1, $2) WHERE (user_id = $3) AND (instance_id = $4)", + expectedStmt: "UPDATE projections.users14_machines SET (name, description) = ($1, $2) WHERE (user_id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ "machine-name", "description", @@ -1643,7 +1643,7 @@ func TestUserProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.users13 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", + expectedStmt: "UPDATE projections.users14 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ anyArg{}, uint64(15), @@ -1652,7 +1652,7 @@ func TestUserProjection_reduces(t *testing.T) { }, }, { - expectedStmt: "UPDATE projections.users13_machines SET name = $1 WHERE (user_id = $2) AND (instance_id = $3)", + expectedStmt: "UPDATE projections.users14_machines SET name = $1 WHERE (user_id = $2) AND (instance_id = $3)", expectedArgs: []interface{}{ "machine-name", "agg-id", @@ -1682,7 +1682,7 @@ func TestUserProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.users13 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", + expectedStmt: "UPDATE projections.users14 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ anyArg{}, uint64(15), @@ -1691,7 +1691,7 @@ func TestUserProjection_reduces(t *testing.T) { }, }, { - expectedStmt: "UPDATE projections.users13_machines SET description = $1 WHERE (user_id = $2) AND (instance_id = $3)", + expectedStmt: "UPDATE projections.users14_machines SET description = $1 WHERE (user_id = $2) AND (instance_id = $3)", expectedArgs: []interface{}{ "description", "agg-id", @@ -1740,7 +1740,7 @@ func TestUserProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.users13 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", + expectedStmt: "UPDATE projections.users14 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ anyArg{}, uint64(15), @@ -1749,7 +1749,7 @@ func TestUserProjection_reduces(t *testing.T) { }, }, { - expectedStmt: "UPDATE projections.users13_machines SET secret = $1 WHERE (user_id = $2) AND (instance_id = $3)", + expectedStmt: "UPDATE projections.users14_machines SET secret = $1 WHERE (user_id = $2) AND (instance_id = $3)", expectedArgs: []interface{}{ "secret", "agg-id", @@ -1779,7 +1779,7 @@ func TestUserProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.users13 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", + expectedStmt: "UPDATE projections.users14 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ anyArg{}, uint64(15), @@ -1788,7 +1788,7 @@ func TestUserProjection_reduces(t *testing.T) { }, }, { - expectedStmt: "UPDATE projections.users13_machines SET secret = $1 WHERE (user_id = $2) AND (instance_id = $3)", + expectedStmt: "UPDATE projections.users14_machines SET secret = $1 WHERE (user_id = $2) AND (instance_id = $3)", expectedArgs: []interface{}{ "secret", "agg-id", @@ -1818,7 +1818,7 @@ func TestUserProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.users13 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", + expectedStmt: "UPDATE projections.users14 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ anyArg{}, uint64(15), @@ -1827,7 +1827,7 @@ func TestUserProjection_reduces(t *testing.T) { }, }, { - expectedStmt: "UPDATE projections.users13_machines SET secret = $1 WHERE (user_id = $2) AND (instance_id = $3)", + expectedStmt: "UPDATE projections.users14_machines SET secret = $1 WHERE (user_id = $2) AND (instance_id = $3)", expectedArgs: []interface{}{ "secret", "agg-id", @@ -1855,7 +1855,7 @@ func TestUserProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "UPDATE projections.users13 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", + expectedStmt: "UPDATE projections.users14 SET (change_date, sequence) = ($1, $2) WHERE (id = $3) AND (instance_id = $4)", expectedArgs: []interface{}{ anyArg{}, uint64(15), @@ -1864,7 +1864,7 @@ func TestUserProjection_reduces(t *testing.T) { }, }, { - expectedStmt: "UPDATE projections.users13_machines SET secret = $1 WHERE (user_id = $2) AND (instance_id = $3)", + expectedStmt: "UPDATE projections.users14_machines SET secret = $1 WHERE (user_id = $2) AND (instance_id = $3)", expectedArgs: []interface{}{ nil, "agg-id", @@ -1875,6 +1875,66 @@ func TestUserProjection_reduces(t *testing.T) { }, }, }, + { + name: "reduceUnsetMFAInitSkipped", + args: args{ + event: getEvent( + testEvent( + user.HumanMFAOTPVerifiedType, + user.AggregateType, + []byte(`{}`), + ), user.HumanOTPVerifiedEventMapper), + }, + reduce: (&userProjection{}).reduceUnsetMFAInitSkipped, + want: wantReduce{ + aggregateType: user.AggregateType, + sequence: 15, + executer: &testExecuter{ + executions: []execution{ + { + expectedStmt: "UPDATE projections.users14_humans SET mfa_init_skipped = $1 WHERE (user_id = $2) AND (instance_id = $3)", + expectedArgs: []interface{}{ + sql.NullTime{}, + "agg-id", + "instance-id", + }, + }, + }, + }, + }, + }, + { + name: "reduceMFAInitSkipped", + args: args{ + event: getEvent( + timedTestEvent( + user.HumanMFAInitSkippedType, + user.AggregateType, + []byte(`{}`), + testNow, + ), user.HumanMFAInitSkippedEventMapper), + }, + reduce: (&userProjection{}).reduceMFAInitSkipped, + want: wantReduce{ + aggregateType: user.AggregateType, + sequence: 15, + executer: &testExecuter{ + executions: []execution{ + { + expectedStmt: "UPDATE projections.users14_humans SET mfa_init_skipped = $1 WHERE (user_id = $2) AND (instance_id = $3)", + expectedArgs: []interface{}{ + sql.NullTime{ + Time: testNow, + Valid: true, + }, + "agg-id", + "instance-id", + }, + }, + }, + }, + }, + }, { name: "org reduceOwnerRemoved", reduce: (&userProjection{}).reduceOwnerRemoved, @@ -1892,7 +1952,7 @@ func TestUserProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "DELETE FROM projections.users13 WHERE (instance_id = $1) AND (resource_owner = $2)", + expectedStmt: "DELETE FROM projections.users14 WHERE (instance_id = $1) AND (resource_owner = $2)", expectedArgs: []interface{}{ "instance-id", "agg-id", @@ -1919,7 +1979,7 @@ func TestUserProjection_reduces(t *testing.T) { executer: &testExecuter{ executions: []execution{ { - expectedStmt: "DELETE FROM projections.users13 WHERE (instance_id = $1)", + expectedStmt: "DELETE FROM projections.users14 WHERE (instance_id = $1)", expectedArgs: []interface{}{ "agg-id", }, diff --git a/internal/query/sessions_test.go b/internal/query/sessions_test.go index 4109969262..ba897e6062 100644 --- a/internal/query/sessions_test.go +++ b/internal/query/sessions_test.go @@ -32,7 +32,7 @@ var ( ` projections.sessions8.user_resource_owner,` + ` projections.sessions8.user_checked_at,` + ` projections.login_names3.login_name,` + - ` projections.users13_humans.display_name,` + + ` projections.users14_humans.display_name,` + ` projections.sessions8.password_checked_at,` + ` projections.sessions8.intent_checked_at,` + ` projections.sessions8.webauthn_checked_at,` + @@ -49,8 +49,8 @@ var ( ` projections.sessions8.expiration` + ` FROM projections.sessions8` + ` LEFT JOIN projections.login_names3 ON projections.sessions8.user_id = projections.login_names3.user_id AND projections.sessions8.instance_id = projections.login_names3.instance_id` + - ` LEFT JOIN projections.users13_humans ON projections.sessions8.user_id = projections.users13_humans.user_id AND projections.sessions8.instance_id = projections.users13_humans.instance_id` + - ` LEFT JOIN projections.users13 ON projections.sessions8.user_id = projections.users13.id AND projections.sessions8.instance_id = projections.users13.instance_id` + + ` LEFT JOIN projections.users14_humans ON projections.sessions8.user_id = projections.users14_humans.user_id AND projections.sessions8.instance_id = projections.users14_humans.instance_id` + + ` LEFT JOIN projections.users14 ON projections.sessions8.user_id = projections.users14.id AND projections.sessions8.instance_id = projections.users14.instance_id` + ` AS OF SYSTEM TIME '-1 ms'`) expectedSessionsQuery = regexp.QuoteMeta(`SELECT projections.sessions8.id,` + ` projections.sessions8.creation_date,` + @@ -63,7 +63,7 @@ var ( ` projections.sessions8.user_resource_owner,` + ` projections.sessions8.user_checked_at,` + ` projections.login_names3.login_name,` + - ` projections.users13_humans.display_name,` + + ` projections.users14_humans.display_name,` + ` projections.sessions8.password_checked_at,` + ` projections.sessions8.intent_checked_at,` + ` projections.sessions8.webauthn_checked_at,` + @@ -80,8 +80,8 @@ var ( ` COUNT(*) OVER ()` + ` FROM projections.sessions8` + ` LEFT JOIN projections.login_names3 ON projections.sessions8.user_id = projections.login_names3.user_id AND projections.sessions8.instance_id = projections.login_names3.instance_id` + - ` LEFT JOIN projections.users13_humans ON projections.sessions8.user_id = projections.users13_humans.user_id AND projections.sessions8.instance_id = projections.users13_humans.instance_id` + - ` LEFT JOIN projections.users13 ON projections.sessions8.user_id = projections.users13.id AND projections.sessions8.instance_id = projections.users13.instance_id` + + ` LEFT JOIN projections.users14_humans ON projections.sessions8.user_id = projections.users14_humans.user_id AND projections.sessions8.instance_id = projections.users14_humans.instance_id` + + ` LEFT JOIN projections.users14 ON projections.sessions8.user_id = projections.users14.id AND projections.sessions8.instance_id = projections.users14.instance_id` + ` AS OF SYSTEM TIME '-1 ms'`) sessionCols = []string{ diff --git a/internal/query/user.go b/internal/query/user.go index b56200d6b0..31a2a58e58 100644 --- a/internal/query/user.go +++ b/internal/query/user.go @@ -55,6 +55,7 @@ type Human struct { IsPhoneVerified bool `json:"is_phone_verified,omitempty"` PasswordChangeRequired bool `json:"password_change_required,omitempty"` PasswordChanged time.Time `json:"password_changed,omitempty"` + MFAInitSkipped time.Time `json:"mfa_init_skipped,omitempty"` } type Profile struct { @@ -271,6 +272,10 @@ var ( name: projection.HumanPasswordChanged, table: humanTable, } + HumanMFAInitSkippedCol = Column{ + name: projection.HumanMFAInitSkipped, + table: humanTable, + } ) var ( @@ -872,6 +877,7 @@ func scanUser(row *sql.Row) (*User, error) { isPhoneVerified := sql.NullBool{} passwordChangeRequired := sql.NullBool{} passwordChanged := sql.NullTime{} + mfaInitSkipped := sql.NullTime{} machineID := sql.NullString{} name := sql.NullString{} @@ -904,6 +910,7 @@ func scanUser(row *sql.Row) (*User, error) { &isPhoneVerified, &passwordChangeRequired, &passwordChanged, + &mfaInitSkipped, &machineID, &name, &description, @@ -936,6 +943,7 @@ func scanUser(row *sql.Row) (*User, error) { IsPhoneVerified: isPhoneVerified.Bool, PasswordChangeRequired: passwordChangeRequired.Bool, PasswordChanged: passwordChanged.Time, + MFAInitSkipped: mfaInitSkipped.Time, } } else if machineID.Valid { u.Machine = &Machine{ @@ -982,6 +990,7 @@ func prepareUserQuery(ctx context.Context, db prepareDatabase) (sq.SelectBuilder HumanIsPhoneVerifiedCol.identifier(), HumanPasswordChangeRequiredCol.identifier(), HumanPasswordChangedCol.identifier(), + HumanMFAInitSkippedCol.identifier(), MachineUserIDCol.identifier(), MachineNameCol.identifier(), MachineDescriptionCol.identifier(), diff --git a/internal/query/user_auth_method_test.go b/internal/query/user_auth_method_test.go index 9504a9db2d..041e4f8e9e 100644 --- a/internal/query/user_auth_method_test.go +++ b/internal/query/user_auth_method_test.go @@ -205,63 +205,63 @@ var ( "method_type", "count", } - prepareActiveAuthMethodTypesStmt = `SELECT projections.users13_notifications.password_set,` + + prepareActiveAuthMethodTypesStmt = `SELECT projections.users14_notifications.password_set,` + ` auth_method_types.method_type,` + ` user_idps_count.count` + - ` FROM projections.users13` + - ` LEFT JOIN projections.users13_notifications ON projections.users13.id = projections.users13_notifications.user_id AND projections.users13.instance_id = projections.users13_notifications.instance_id` + + ` FROM projections.users14` + + ` LEFT JOIN projections.users14_notifications ON projections.users14.id = projections.users14_notifications.user_id AND projections.users14.instance_id = projections.users14_notifications.instance_id` + ` LEFT JOIN (SELECT DISTINCT(auth_method_types.method_type), auth_method_types.user_id, auth_method_types.instance_id FROM projections.user_auth_methods5 AS auth_method_types` + ` WHERE auth_method_types.state = $1) AS auth_method_types` + - ` ON auth_method_types.user_id = projections.users13.id AND auth_method_types.instance_id = projections.users13.instance_id` + + ` ON auth_method_types.user_id = projections.users14.id AND auth_method_types.instance_id = projections.users14.instance_id` + ` LEFT JOIN (SELECT user_idps_count.user_id, user_idps_count.instance_id, COUNT(user_idps_count.user_id) AS count FROM projections.idp_user_links3 AS user_idps_count` + ` GROUP BY user_idps_count.user_id, user_idps_count.instance_id) AS user_idps_count` + - ` ON user_idps_count.user_id = projections.users13.id AND user_idps_count.instance_id = projections.users13.instance_id` + + ` ON user_idps_count.user_id = projections.users14.id AND user_idps_count.instance_id = projections.users14.instance_id` + ` AS OF SYSTEM TIME '-1 ms` prepareActiveAuthMethodTypesCols = []string{ "password_set", "method_type", "idps_count", } - prepareActiveAuthMethodTypesDomainStmt = `SELECT projections.users13_notifications.password_set,` + + prepareActiveAuthMethodTypesDomainStmt = `SELECT projections.users14_notifications.password_set,` + ` auth_method_types.method_type,` + ` user_idps_count.count` + - ` FROM projections.users13` + - ` LEFT JOIN projections.users13_notifications ON projections.users13.id = projections.users13_notifications.user_id AND projections.users13.instance_id = projections.users13_notifications.instance_id` + + ` FROM projections.users14` + + ` LEFT JOIN projections.users14_notifications ON projections.users14.id = projections.users14_notifications.user_id AND projections.users14.instance_id = projections.users14_notifications.instance_id` + ` LEFT JOIN (SELECT DISTINCT(auth_method_types.method_type), auth_method_types.user_id, auth_method_types.instance_id FROM projections.user_auth_methods5 AS auth_method_types` + ` WHERE auth_method_types.state = $1 AND (auth_method_types.domain IS NULL OR auth_method_types.domain = $2 OR auth_method_types.domain = $3)) AS auth_method_types` + - ` ON auth_method_types.user_id = projections.users13.id AND auth_method_types.instance_id = projections.users13.instance_id` + + ` ON auth_method_types.user_id = projections.users14.id AND auth_method_types.instance_id = projections.users14.instance_id` + ` LEFT JOIN (SELECT user_idps_count.user_id, user_idps_count.instance_id, COUNT(user_idps_count.user_id) AS count FROM projections.idp_user_links3 AS user_idps_count` + ` GROUP BY user_idps_count.user_id, user_idps_count.instance_id) AS user_idps_count` + - ` ON user_idps_count.user_id = projections.users13.id AND user_idps_count.instance_id = projections.users13.instance_id` + + ` ON user_idps_count.user_id = projections.users14.id AND user_idps_count.instance_id = projections.users14.instance_id` + ` AS OF SYSTEM TIME '-1 ms` prepareActiveAuthMethodTypesDomainCols = []string{ "password_set", "method_type", "idps_count", } - prepareActiveAuthMethodTypesDomainExternalStmt = `SELECT projections.users13_notifications.password_set,` + + prepareActiveAuthMethodTypesDomainExternalStmt = `SELECT projections.users14_notifications.password_set,` + ` auth_method_types.method_type,` + ` user_idps_count.count` + - ` FROM projections.users13` + - ` LEFT JOIN projections.users13_notifications ON projections.users13.id = projections.users13_notifications.user_id AND projections.users13.instance_id = projections.users13_notifications.instance_id` + + ` FROM projections.users14` + + ` LEFT JOIN projections.users14_notifications ON projections.users14.id = projections.users14_notifications.user_id AND projections.users14.instance_id = projections.users14_notifications.instance_id` + ` LEFT JOIN (SELECT DISTINCT(auth_method_types.method_type), auth_method_types.user_id, auth_method_types.instance_id FROM projections.user_auth_methods5 AS auth_method_types` + ` WHERE auth_method_types.state = $1 AND (auth_method_types.domain IS NULL OR auth_method_types.domain = $2)) AS auth_method_types` + - ` ON auth_method_types.user_id = projections.users13.id AND auth_method_types.instance_id = projections.users13.instance_id` + + ` ON auth_method_types.user_id = projections.users14.id AND auth_method_types.instance_id = projections.users14.instance_id` + ` LEFT JOIN (SELECT user_idps_count.user_id, user_idps_count.instance_id, COUNT(user_idps_count.user_id) AS count FROM projections.idp_user_links3 AS user_idps_count` + ` GROUP BY user_idps_count.user_id, user_idps_count.instance_id) AS user_idps_count` + - ` ON user_idps_count.user_id = projections.users13.id AND user_idps_count.instance_id = projections.users13.instance_id` + + ` ON user_idps_count.user_id = projections.users14.id AND user_idps_count.instance_id = projections.users14.instance_id` + ` AS OF SYSTEM TIME '-1 ms` prepareActiveAuthMethodTypesDomainExternalCols = []string{ "password_set", "method_type", "idps_count", } - prepareAuthMethodTypesRequiredStmt = `SELECT projections.users13.type,` + + prepareAuthMethodTypesRequiredStmt = `SELECT projections.users14.type,` + ` auth_methods_force_mfa.force_mfa,` + ` auth_methods_force_mfa.force_mfa_local_only` + - ` FROM projections.users13` + + ` FROM projections.users14` + ` LEFT JOIN (SELECT auth_methods_force_mfa.force_mfa, auth_methods_force_mfa.force_mfa_local_only, auth_methods_force_mfa.instance_id, auth_methods_force_mfa.aggregate_id, auth_methods_force_mfa.is_default FROM projections.login_policies5 AS auth_methods_force_mfa) AS auth_methods_force_mfa` + - ` ON (auth_methods_force_mfa.aggregate_id = projections.users13.instance_id OR auth_methods_force_mfa.aggregate_id = projections.users13.resource_owner) AND auth_methods_force_mfa.instance_id = projections.users13.instance_id` + + ` ON (auth_methods_force_mfa.aggregate_id = projections.users14.instance_id OR auth_methods_force_mfa.aggregate_id = projections.users14.resource_owner) AND auth_methods_force_mfa.instance_id = projections.users14.instance_id` + ` ORDER BY auth_methods_force_mfa.is_default LIMIT 1 ` prepareAuthMethodTypesRequiredCols = []string{ diff --git a/internal/query/user_by_id.sql b/internal/query/user_by_id.sql index 2871f08893..366e6552f6 100644 --- a/internal/query/user_by_id.sql +++ b/internal/query/user_by_id.sql @@ -60,20 +60,21 @@ SELECT , h.is_phone_verified , h.password_change_required , h.password_changed + , h.mfa_init_skipped , m.user_id , m.name , m.description , m.secret , m.access_token_type , count(*) OVER () -FROM projections.users13 u +FROM projections.users14 u LEFT JOIN - projections.users13_humans h + projections.users14_humans h ON u.id = h.user_id AND u.instance_id = h.instance_id LEFT JOIN - projections.users13_machines m + projections.users14_machines m ON u.id = m.user_id AND u.instance_id = m.instance_id diff --git a/internal/query/user_by_login_name.sql b/internal/query/user_by_login_name.sql index 6890d83863..a37c213612 100644 --- a/internal/query/user_by_login_name.sql +++ b/internal/query/user_by_login_name.sql @@ -75,6 +75,7 @@ SELECT , h.is_phone_verified , h.password_change_required , h.password_changed + , h.mfa_init_skipped , m.user_id , m.name , m.description @@ -83,17 +84,17 @@ SELECT , count(*) OVER () FROM found_users fu JOIN - projections.users13 u + projections.users14 u ON fu.id = u.id AND fu.instance_id = u.instance_id LEFT JOIN - projections.users13_humans h + projections.users14_humans h ON fu.id = h.user_id AND fu.instance_id = h.instance_id LEFT JOIN - projections.users13_machines m + projections.users14_machines m ON fu.id = m.user_id AND fu.instance_id = m.instance_id diff --git a/internal/query/user_grant_test.go b/internal/query/user_grant_test.go index 3bc9b38f69..6cfa0b563b 100644 --- a/internal/query/user_grant_test.go +++ b/internal/query/user_grant_test.go @@ -23,14 +23,14 @@ var ( ", projections.user_grants5.roles" + ", projections.user_grants5.state" + ", projections.user_grants5.user_id" + - ", projections.users13.username" + - ", projections.users13.type" + - ", projections.users13.resource_owner" + - ", projections.users13_humans.first_name" + - ", projections.users13_humans.last_name" + - ", projections.users13_humans.email" + - ", projections.users13_humans.display_name" + - ", projections.users13_humans.avatar_key" + + ", projections.users14.username" + + ", projections.users14.type" + + ", projections.users14.resource_owner" + + ", projections.users14_humans.first_name" + + ", projections.users14_humans.last_name" + + ", projections.users14_humans.email" + + ", projections.users14_humans.display_name" + + ", projections.users14_humans.avatar_key" + ", projections.login_names3.login_name" + ", projections.user_grants5.resource_owner" + ", projections.orgs1.name" + @@ -41,11 +41,11 @@ var ( ", granted_orgs.name" + ", granted_orgs.primary_domain" + " FROM projections.user_grants5" + - " LEFT JOIN projections.users13 ON projections.user_grants5.user_id = projections.users13.id AND projections.user_grants5.instance_id = projections.users13.instance_id" + - " LEFT JOIN projections.users13_humans ON projections.user_grants5.user_id = projections.users13_humans.user_id AND projections.user_grants5.instance_id = projections.users13_humans.instance_id" + + " LEFT JOIN projections.users14 ON projections.user_grants5.user_id = projections.users14.id AND projections.user_grants5.instance_id = projections.users14.instance_id" + + " LEFT JOIN projections.users14_humans ON projections.user_grants5.user_id = projections.users14_humans.user_id AND projections.user_grants5.instance_id = projections.users14_humans.instance_id" + " LEFT JOIN projections.orgs1 ON projections.user_grants5.resource_owner = projections.orgs1.id AND projections.user_grants5.instance_id = projections.orgs1.instance_id" + " LEFT JOIN projections.projects4 ON projections.user_grants5.project_id = projections.projects4.id AND projections.user_grants5.instance_id = projections.projects4.instance_id" + - " LEFT JOIN projections.orgs1 AS granted_orgs ON projections.users13.resource_owner = granted_orgs.id AND projections.users13.instance_id = granted_orgs.instance_id" + + " LEFT JOIN projections.orgs1 AS granted_orgs ON projections.users14.resource_owner = granted_orgs.id AND projections.users14.instance_id = granted_orgs.instance_id" + " LEFT JOIN projections.login_names3 ON projections.user_grants5.user_id = projections.login_names3.user_id AND projections.user_grants5.instance_id = projections.login_names3.instance_id" + ` AS OF SYSTEM TIME '-1 ms' ` + " WHERE projections.login_names3.is_primary = $1") @@ -85,14 +85,14 @@ var ( ", projections.user_grants5.roles" + ", projections.user_grants5.state" + ", projections.user_grants5.user_id" + - ", projections.users13.username" + - ", projections.users13.type" + - ", projections.users13.resource_owner" + - ", projections.users13_humans.first_name" + - ", projections.users13_humans.last_name" + - ", projections.users13_humans.email" + - ", projections.users13_humans.display_name" + - ", projections.users13_humans.avatar_key" + + ", projections.users14.username" + + ", projections.users14.type" + + ", projections.users14.resource_owner" + + ", projections.users14_humans.first_name" + + ", projections.users14_humans.last_name" + + ", projections.users14_humans.email" + + ", projections.users14_humans.display_name" + + ", projections.users14_humans.avatar_key" + ", projections.login_names3.login_name" + ", projections.user_grants5.resource_owner" + ", projections.orgs1.name" + @@ -104,11 +104,11 @@ var ( ", granted_orgs.primary_domain" + ", COUNT(*) OVER ()" + " FROM projections.user_grants5" + - " LEFT JOIN projections.users13 ON projections.user_grants5.user_id = projections.users13.id AND projections.user_grants5.instance_id = projections.users13.instance_id" + - " LEFT JOIN projections.users13_humans ON projections.user_grants5.user_id = projections.users13_humans.user_id AND projections.user_grants5.instance_id = projections.users13_humans.instance_id" + + " LEFT JOIN projections.users14 ON projections.user_grants5.user_id = projections.users14.id AND projections.user_grants5.instance_id = projections.users14.instance_id" + + " LEFT JOIN projections.users14_humans ON projections.user_grants5.user_id = projections.users14_humans.user_id AND projections.user_grants5.instance_id = projections.users14_humans.instance_id" + " LEFT JOIN projections.orgs1 ON projections.user_grants5.resource_owner = projections.orgs1.id AND projections.user_grants5.instance_id = projections.orgs1.instance_id" + " LEFT JOIN projections.projects4 ON projections.user_grants5.project_id = projections.projects4.id AND projections.user_grants5.instance_id = projections.projects4.instance_id" + - " LEFT JOIN projections.orgs1 AS granted_orgs ON projections.users13.resource_owner = granted_orgs.id AND projections.users13.instance_id = granted_orgs.instance_id" + + " LEFT JOIN projections.orgs1 AS granted_orgs ON projections.users14.resource_owner = granted_orgs.id AND projections.users14.instance_id = granted_orgs.instance_id" + " LEFT JOIN projections.login_names3 ON projections.user_grants5.user_id = projections.login_names3.user_id AND projections.user_grants5.instance_id = projections.login_names3.instance_id" + ` AS OF SYSTEM TIME '-1 ms' ` + " WHERE projections.login_names3.is_primary = $1") diff --git a/internal/query/user_notify_by_id.sql b/internal/query/user_notify_by_id.sql index 19c2ebe82b..10aa60ee60 100644 --- a/internal/query/user_notify_by_id.sql +++ b/internal/query/user_notify_by_id.sql @@ -62,14 +62,14 @@ SELECT , n.verified_phone , n.password_set , count(*) OVER () -FROM projections.users13 u +FROM projections.users14 u LEFT JOIN - projections.users13_humans h + projections.users14_humans h ON u.id = h.user_id AND u.instance_id = h.instance_id LEFT JOIN - projections.users13_notifications n + projections.users14_notifications n ON u.id = n.user_id AND u.instance_id = n.instance_id diff --git a/internal/query/user_notify_by_login_name.sql b/internal/query/user_notify_by_login_name.sql index 154907d620..5b23cd61a7 100644 --- a/internal/query/user_notify_by_login_name.sql +++ b/internal/query/user_notify_by_login_name.sql @@ -78,17 +78,17 @@ SELECT , count(*) OVER () FROM found_users fu JOIN - projections.users13 u + projections.users14 u ON fu.id = u.id AND fu.instance_id = u.instance_id LEFT JOIN - projections.users13_humans h + projections.users14_humans h ON fu.id = h.user_id AND fu.instance_id = h.instance_id LEFT JOIN - projections.users13_notifications n + projections.users14_notifications n ON fu.id = n.user_id AND fu.instance_id = n.instance_id diff --git a/internal/query/user_test.go b/internal/query/user_test.go index 1b26511497..16b08611f0 100644 --- a/internal/query/user_test.go +++ b/internal/query/user_test.go @@ -229,45 +229,46 @@ var ( preferredLoginNameQuery = `SELECT preferred_login_name.user_id, preferred_login_name.login_name, preferred_login_name.instance_id` + ` FROM projections.login_names3 AS preferred_login_name` + ` WHERE preferred_login_name.is_primary = $1` - userQuery = `SELECT projections.users13.id,` + - ` projections.users13.creation_date,` + - ` projections.users13.change_date,` + - ` projections.users13.resource_owner,` + - ` projections.users13.sequence,` + - ` projections.users13.state,` + - ` projections.users13.type,` + - ` projections.users13.username,` + + userQuery = `SELECT projections.users14.id,` + + ` projections.users14.creation_date,` + + ` projections.users14.change_date,` + + ` projections.users14.resource_owner,` + + ` projections.users14.sequence,` + + ` projections.users14.state,` + + ` projections.users14.type,` + + ` projections.users14.username,` + ` login_names.loginnames,` + ` preferred_login_name.login_name,` + - ` projections.users13_humans.user_id,` + - ` projections.users13_humans.first_name,` + - ` projections.users13_humans.last_name,` + - ` projections.users13_humans.nick_name,` + - ` projections.users13_humans.display_name,` + - ` projections.users13_humans.preferred_language,` + - ` projections.users13_humans.gender,` + - ` projections.users13_humans.avatar_key,` + - ` projections.users13_humans.email,` + - ` projections.users13_humans.is_email_verified,` + - ` projections.users13_humans.phone,` + - ` projections.users13_humans.is_phone_verified,` + - ` projections.users13_humans.password_change_required,` + - ` projections.users13_humans.password_changed,` + - ` projections.users13_machines.user_id,` + - ` projections.users13_machines.name,` + - ` projections.users13_machines.description,` + - ` projections.users13_machines.secret,` + - ` projections.users13_machines.access_token_type,` + + ` projections.users14_humans.user_id,` + + ` projections.users14_humans.first_name,` + + ` projections.users14_humans.last_name,` + + ` projections.users14_humans.nick_name,` + + ` projections.users14_humans.display_name,` + + ` projections.users14_humans.preferred_language,` + + ` projections.users14_humans.gender,` + + ` projections.users14_humans.avatar_key,` + + ` projections.users14_humans.email,` + + ` projections.users14_humans.is_email_verified,` + + ` projections.users14_humans.phone,` + + ` projections.users14_humans.is_phone_verified,` + + ` projections.users14_humans.password_change_required,` + + ` projections.users14_humans.password_changed,` + + ` projections.users14_humans.mfa_init_skipped,` + + ` projections.users14_machines.user_id,` + + ` projections.users14_machines.name,` + + ` projections.users14_machines.description,` + + ` projections.users14_machines.secret,` + + ` projections.users14_machines.access_token_type,` + ` COUNT(*) OVER ()` + - ` FROM projections.users13` + - ` LEFT JOIN projections.users13_humans ON projections.users13.id = projections.users13_humans.user_id AND projections.users13.instance_id = projections.users13_humans.instance_id` + - ` LEFT JOIN projections.users13_machines ON projections.users13.id = projections.users13_machines.user_id AND projections.users13.instance_id = projections.users13_machines.instance_id` + + ` FROM projections.users14` + + ` LEFT JOIN projections.users14_humans ON projections.users14.id = projections.users14_humans.user_id AND projections.users14.instance_id = projections.users14_humans.instance_id` + + ` LEFT JOIN projections.users14_machines ON projections.users14.id = projections.users14_machines.user_id AND projections.users14.instance_id = projections.users14_machines.instance_id` + ` LEFT JOIN` + ` (` + loginNamesQuery + `) AS login_names` + - ` ON login_names.user_id = projections.users13.id AND login_names.instance_id = projections.users13.instance_id` + + ` ON login_names.user_id = projections.users14.id AND login_names.instance_id = projections.users14.instance_id` + ` LEFT JOIN` + ` (` + preferredLoginNameQuery + `) AS preferred_login_name` + - ` ON preferred_login_name.user_id = projections.users13.id AND preferred_login_name.instance_id = projections.users13.instance_id` + + ` ON preferred_login_name.user_id = projections.users14.id AND preferred_login_name.instance_id = projections.users14.instance_id` + ` AS OF SYSTEM TIME '-1 ms'` userCols = []string{ "id", @@ -295,6 +296,7 @@ var ( "is_phone_verified", "password_change_required", "password_changed", + "mfa_init_skipped", // machine "user_id", "name", @@ -303,21 +305,21 @@ var ( "access_token_type", "count", } - profileQuery = `SELECT projections.users13.id,` + - ` projections.users13.creation_date,` + - ` projections.users13.change_date,` + - ` projections.users13.resource_owner,` + - ` projections.users13.sequence,` + - ` projections.users13_humans.user_id,` + - ` projections.users13_humans.first_name,` + - ` projections.users13_humans.last_name,` + - ` projections.users13_humans.nick_name,` + - ` projections.users13_humans.display_name,` + - ` projections.users13_humans.preferred_language,` + - ` projections.users13_humans.gender,` + - ` projections.users13_humans.avatar_key` + - ` FROM projections.users13` + - ` LEFT JOIN projections.users13_humans ON projections.users13.id = projections.users13_humans.user_id AND projections.users13.instance_id = projections.users13_humans.instance_id` + + profileQuery = `SELECT projections.users14.id,` + + ` projections.users14.creation_date,` + + ` projections.users14.change_date,` + + ` projections.users14.resource_owner,` + + ` projections.users14.sequence,` + + ` projections.users14_humans.user_id,` + + ` projections.users14_humans.first_name,` + + ` projections.users14_humans.last_name,` + + ` projections.users14_humans.nick_name,` + + ` projections.users14_humans.display_name,` + + ` projections.users14_humans.preferred_language,` + + ` projections.users14_humans.gender,` + + ` projections.users14_humans.avatar_key` + + ` FROM projections.users14` + + ` LEFT JOIN projections.users14_humans ON projections.users14.id = projections.users14_humans.user_id AND projections.users14.instance_id = projections.users14_humans.instance_id` + ` AS OF SYSTEM TIME '-1 ms'` profileCols = []string{ "id", @@ -334,16 +336,16 @@ var ( "gender", "avatar_key", } - emailQuery = `SELECT projections.users13.id,` + - ` projections.users13.creation_date,` + - ` projections.users13.change_date,` + - ` projections.users13.resource_owner,` + - ` projections.users13.sequence,` + - ` projections.users13_humans.user_id,` + - ` projections.users13_humans.email,` + - ` projections.users13_humans.is_email_verified` + - ` FROM projections.users13` + - ` LEFT JOIN projections.users13_humans ON projections.users13.id = projections.users13_humans.user_id AND projections.users13.instance_id = projections.users13_humans.instance_id` + + emailQuery = `SELECT projections.users14.id,` + + ` projections.users14.creation_date,` + + ` projections.users14.change_date,` + + ` projections.users14.resource_owner,` + + ` projections.users14.sequence,` + + ` projections.users14_humans.user_id,` + + ` projections.users14_humans.email,` + + ` projections.users14_humans.is_email_verified` + + ` FROM projections.users14` + + ` LEFT JOIN projections.users14_humans ON projections.users14.id = projections.users14_humans.user_id AND projections.users14.instance_id = projections.users14_humans.instance_id` + ` AS OF SYSTEM TIME '-1 ms'` emailCols = []string{ "id", @@ -355,16 +357,16 @@ var ( "email", "is_email_verified", } - phoneQuery = `SELECT projections.users13.id,` + - ` projections.users13.creation_date,` + - ` projections.users13.change_date,` + - ` projections.users13.resource_owner,` + - ` projections.users13.sequence,` + - ` projections.users13_humans.user_id,` + - ` projections.users13_humans.phone,` + - ` projections.users13_humans.is_phone_verified` + - ` FROM projections.users13` + - ` LEFT JOIN projections.users13_humans ON projections.users13.id = projections.users13_humans.user_id AND projections.users13.instance_id = projections.users13_humans.instance_id` + + phoneQuery = `SELECT projections.users14.id,` + + ` projections.users14.creation_date,` + + ` projections.users14.change_date,` + + ` projections.users14.resource_owner,` + + ` projections.users14.sequence,` + + ` projections.users14_humans.user_id,` + + ` projections.users14_humans.phone,` + + ` projections.users14_humans.is_phone_verified` + + ` FROM projections.users14` + + ` LEFT JOIN projections.users14_humans ON projections.users14.id = projections.users14_humans.user_id AND projections.users14.instance_id = projections.users14_humans.instance_id` + ` AS OF SYSTEM TIME '-1 ms'` phoneCols = []string{ "id", @@ -376,14 +378,14 @@ var ( "phone", "is_phone_verified", } - userUniqueQuery = `SELECT projections.users13.id,` + - ` projections.users13.state,` + - ` projections.users13.username,` + - ` projections.users13_humans.user_id,` + - ` projections.users13_humans.email,` + - ` projections.users13_humans.is_email_verified` + - ` FROM projections.users13` + - ` LEFT JOIN projections.users13_humans ON projections.users13.id = projections.users13_humans.user_id AND projections.users13.instance_id = projections.users13_humans.instance_id` + + userUniqueQuery = `SELECT projections.users14.id,` + + ` projections.users14.state,` + + ` projections.users14.username,` + + ` projections.users14_humans.user_id,` + + ` projections.users14_humans.email,` + + ` projections.users14_humans.is_email_verified` + + ` FROM projections.users14` + + ` LEFT JOIN projections.users14_humans ON projections.users14.id = projections.users14_humans.user_id AND projections.users14.instance_id = projections.users14_humans.instance_id` + ` AS OF SYSTEM TIME '-1 ms'` userUniqueCols = []string{ "id", @@ -393,40 +395,40 @@ var ( "email", "is_email_verified", } - notifyUserQuery = `SELECT projections.users13.id,` + - ` projections.users13.creation_date,` + - ` projections.users13.change_date,` + - ` projections.users13.resource_owner,` + - ` projections.users13.sequence,` + - ` projections.users13.state,` + - ` projections.users13.type,` + - ` projections.users13.username,` + + notifyUserQuery = `SELECT projections.users14.id,` + + ` projections.users14.creation_date,` + + ` projections.users14.change_date,` + + ` projections.users14.resource_owner,` + + ` projections.users14.sequence,` + + ` projections.users14.state,` + + ` projections.users14.type,` + + ` projections.users14.username,` + ` login_names.loginnames,` + ` preferred_login_name.login_name,` + - ` projections.users13_humans.user_id,` + - ` projections.users13_humans.first_name,` + - ` projections.users13_humans.last_name,` + - ` projections.users13_humans.nick_name,` + - ` projections.users13_humans.display_name,` + - ` projections.users13_humans.preferred_language,` + - ` projections.users13_humans.gender,` + - ` projections.users13_humans.avatar_key,` + - ` projections.users13_notifications.user_id,` + - ` projections.users13_notifications.last_email,` + - ` projections.users13_notifications.verified_email,` + - ` projections.users13_notifications.last_phone,` + - ` projections.users13_notifications.verified_phone,` + - ` projections.users13_notifications.password_set,` + + ` projections.users14_humans.user_id,` + + ` projections.users14_humans.first_name,` + + ` projections.users14_humans.last_name,` + + ` projections.users14_humans.nick_name,` + + ` projections.users14_humans.display_name,` + + ` projections.users14_humans.preferred_language,` + + ` projections.users14_humans.gender,` + + ` projections.users14_humans.avatar_key,` + + ` projections.users14_notifications.user_id,` + + ` projections.users14_notifications.last_email,` + + ` projections.users14_notifications.verified_email,` + + ` projections.users14_notifications.last_phone,` + + ` projections.users14_notifications.verified_phone,` + + ` projections.users14_notifications.password_set,` + ` COUNT(*) OVER ()` + - ` FROM projections.users13` + - ` LEFT JOIN projections.users13_humans ON projections.users13.id = projections.users13_humans.user_id AND projections.users13.instance_id = projections.users13_humans.instance_id` + - ` LEFT JOIN projections.users13_notifications ON projections.users13.id = projections.users13_notifications.user_id AND projections.users13.instance_id = projections.users13_notifications.instance_id` + + ` FROM projections.users14` + + ` LEFT JOIN projections.users14_humans ON projections.users14.id = projections.users14_humans.user_id AND projections.users14.instance_id = projections.users14_humans.instance_id` + + ` LEFT JOIN projections.users14_notifications ON projections.users14.id = projections.users14_notifications.user_id AND projections.users14.instance_id = projections.users14_notifications.instance_id` + ` LEFT JOIN` + ` (` + loginNamesQuery + `) AS login_names` + - ` ON login_names.user_id = projections.users13.id AND login_names.instance_id = projections.users13.instance_id` + + ` ON login_names.user_id = projections.users14.id AND login_names.instance_id = projections.users14.instance_id` + ` LEFT JOIN` + ` (` + preferredLoginNameQuery + `) AS preferred_login_name` + - ` ON preferred_login_name.user_id = projections.users13.id AND preferred_login_name.instance_id = projections.users13.instance_id` + + ` ON preferred_login_name.user_id = projections.users14.id AND preferred_login_name.instance_id = projections.users14.instance_id` + ` AS OF SYSTEM TIME '-1 ms'` notifyUserCols = []string{ "id", @@ -457,45 +459,45 @@ var ( "password_set", "count", } - usersQuery = `SELECT projections.users13.id,` + - ` projections.users13.creation_date,` + - ` projections.users13.change_date,` + - ` projections.users13.resource_owner,` + - ` projections.users13.sequence,` + - ` projections.users13.state,` + - ` projections.users13.type,` + - ` projections.users13.username,` + + usersQuery = `SELECT projections.users14.id,` + + ` projections.users14.creation_date,` + + ` projections.users14.change_date,` + + ` projections.users14.resource_owner,` + + ` projections.users14.sequence,` + + ` projections.users14.state,` + + ` projections.users14.type,` + + ` projections.users14.username,` + ` login_names.loginnames,` + ` preferred_login_name.login_name,` + - ` projections.users13_humans.user_id,` + - ` projections.users13_humans.first_name,` + - ` projections.users13_humans.last_name,` + - ` projections.users13_humans.nick_name,` + - ` projections.users13_humans.display_name,` + - ` projections.users13_humans.preferred_language,` + - ` projections.users13_humans.gender,` + - ` projections.users13_humans.avatar_key,` + - ` projections.users13_humans.email,` + - ` projections.users13_humans.is_email_verified,` + - ` projections.users13_humans.phone,` + - ` projections.users13_humans.is_phone_verified,` + - ` projections.users13_humans.password_change_required,` + - ` projections.users13_humans.password_changed,` + - ` projections.users13_machines.user_id,` + - ` projections.users13_machines.name,` + - ` projections.users13_machines.description,` + - ` projections.users13_machines.secret,` + - ` projections.users13_machines.access_token_type,` + + ` projections.users14_humans.user_id,` + + ` projections.users14_humans.first_name,` + + ` projections.users14_humans.last_name,` + + ` projections.users14_humans.nick_name,` + + ` projections.users14_humans.display_name,` + + ` projections.users14_humans.preferred_language,` + + ` projections.users14_humans.gender,` + + ` projections.users14_humans.avatar_key,` + + ` projections.users14_humans.email,` + + ` projections.users14_humans.is_email_verified,` + + ` projections.users14_humans.phone,` + + ` projections.users14_humans.is_phone_verified,` + + ` projections.users14_humans.password_change_required,` + + ` projections.users14_humans.password_changed,` + + ` projections.users14_machines.user_id,` + + ` projections.users14_machines.name,` + + ` projections.users14_machines.description,` + + ` projections.users14_machines.secret,` + + ` projections.users14_machines.access_token_type,` + ` COUNT(*) OVER ()` + - ` FROM projections.users13` + - ` LEFT JOIN projections.users13_humans ON projections.users13.id = projections.users13_humans.user_id AND projections.users13.instance_id = projections.users13_humans.instance_id` + - ` LEFT JOIN projections.users13_machines ON projections.users13.id = projections.users13_machines.user_id AND projections.users13.instance_id = projections.users13_machines.instance_id` + + ` FROM projections.users14` + + ` LEFT JOIN projections.users14_humans ON projections.users14.id = projections.users14_humans.user_id AND projections.users14.instance_id = projections.users14_humans.instance_id` + + ` LEFT JOIN projections.users14_machines ON projections.users14.id = projections.users14_machines.user_id AND projections.users14.instance_id = projections.users14_machines.instance_id` + ` LEFT JOIN` + ` (` + loginNamesQuery + `) AS login_names` + - ` ON login_names.user_id = projections.users13.id AND login_names.instance_id = projections.users13.instance_id` + + ` ON login_names.user_id = projections.users14.id AND login_names.instance_id = projections.users14.instance_id` + ` LEFT JOIN` + ` (` + preferredLoginNameQuery + `) AS preferred_login_name` + - ` ON preferred_login_name.user_id = projections.users13.id AND preferred_login_name.instance_id = projections.users13.instance_id` + + ` ON preferred_login_name.user_id = projections.users14.id AND preferred_login_name.instance_id = projections.users14.instance_id` + ` AS OF SYSTEM TIME '-1 ms'` usersCols = []string{ "id", @@ -531,7 +533,7 @@ var ( "access_token_type", "count", } - countUsersQuery = "SELECT COUNT(*) OVER () FROM projections.users13" + countUsersQuery = "SELECT COUNT(*) OVER () FROM projections.users14" countUsersCols = []string{"count"} ) @@ -597,6 +599,7 @@ func Test_UserPrepares(t *testing.T) { true, true, testNow, + testNow, // machine nil, nil, @@ -632,6 +635,7 @@ func Test_UserPrepares(t *testing.T) { IsPhoneVerified: true, PasswordChangeRequired: true, PasswordChanged: testNow, + MFAInitSkipped: testNow, }, }, }, @@ -668,6 +672,7 @@ func Test_UserPrepares(t *testing.T) { nil, nil, nil, + nil, // machine "id", "name", @@ -730,6 +735,7 @@ func Test_UserPrepares(t *testing.T) { nil, nil, nil, + nil, // machine "id", "name", diff --git a/internal/query/userinfo_by_id.sql b/internal/query/userinfo_by_id.sql index cd7f301c72..fec5bc9684 100644 --- a/internal/query/userinfo_by_id.sql +++ b/internal/query/userinfo_by_id.sql @@ -1,6 +1,6 @@ with usr as ( select u.id, u.creation_date, u.change_date, u.sequence, u.state, u.resource_owner, u.username, n.login_name as preferred_login_name - from projections.users13 u + from projections.users14 u left join projections.login_names3 n on u.id = n.user_id and u.instance_id = n.instance_id where u.id = $1 and u.state = 1 -- only allow active users and u.instance_id = $2 @@ -9,7 +9,7 @@ with usr as ( human as ( select $1 as user_id, row_to_json(r) as human from ( select first_name, last_name, nick_name, display_name, avatar_key, preferred_language, gender, email, is_email_verified, phone, is_phone_verified - from projections.users13_humans + from projections.users14_humans where user_id = $1 and instance_id = $2 ) r @@ -17,7 +17,7 @@ human as ( machine as ( select $1 as user_id, row_to_json(r) as machine from ( select name, description - from projections.users13_machines + from projections.users14_machines where user_id = $1 and instance_id = $2 ) r diff --git a/internal/repository/user/human_mfa_otp.go b/internal/repository/user/human_mfa_otp.go index 93706d714e..cd79e5fa35 100644 --- a/internal/repository/user/human_mfa_otp.go +++ b/internal/repository/user/human_mfa_otp.go @@ -103,9 +103,10 @@ func NewHumanOTPVerifiedEvent( } func HumanOTPVerifiedEventMapper(event eventstore.Event) (eventstore.Event, error) { - return &HumanOTPVerifiedEvent{ + out := &HumanOTPVerifiedEvent{ BaseEvent: *eventstore.BaseEventFromRepo(event), - }, nil + } + return out, nil } type HumanOTPRemovedEvent struct { diff --git a/internal/user/repository/view/user_by_id.sql b/internal/user/repository/view/user_by_id.sql index bd34f77d80..e56d4e0272 100644 --- a/internal/user/repository/view/user_by_id.sql +++ b/internal/user/repository/view/user_by_id.sql @@ -74,18 +74,18 @@ SELECT , u.instance_id , (SELECT EXISTS (SELECT true FROM verified_auth_methods WHERE method_type = 6)) AS otp_sms_added , (SELECT EXISTS (SELECT true FROM verified_auth_methods WHERE method_type = 7)) AS otp_email_added -FROM projections.users13 u - LEFT JOIN projections.users13_humans h +FROM projections.users14 u + LEFT JOIN projections.users14_humans h ON u.instance_id = h.instance_id AND u.id = h.user_id - LEFT JOIN projections.users13_notifications n + LEFT JOIN projections.users14_notifications n ON u.instance_id = n.instance_id AND u.id = n.user_id LEFT JOIN projections.login_names3 l ON u.instance_id = l.instance_id AND u.id = l.user_id AND l.is_primary = true - LEFT JOIN projections.users13_machines m + LEFT JOIN projections.users14_machines m ON u.instance_id = m.instance_id AND u.id = m.user_id LEFT JOIN auth.users3 au diff --git a/internal/user/repository/view/user_session_by_id.sql b/internal/user/repository/view/user_session_by_id.sql index a41d480936..5426fe9a03 100644 --- a/internal/user/repository/view/user_session_by_id.sql +++ b/internal/user/repository/view/user_session_by_id.sql @@ -20,8 +20,8 @@ SELECT s.creation_date, s.instance_id, s.id FROM auth.user_sessions s - LEFT JOIN projections.users13 u ON s.user_id = u.id AND s.instance_id = u.instance_id - LEFT JOIN projections.users13_humans h ON s.user_id = h.user_id AND s.instance_id = h.instance_id + LEFT JOIN projections.users14 u ON s.user_id = u.id AND s.instance_id = u.instance_id + LEFT JOIN projections.users14_humans h ON s.user_id = h.user_id AND s.instance_id = h.instance_id LEFT JOIN projections.login_names3 l ON s.user_id = l.user_id AND s.instance_id = l.instance_id AND l.is_primary = true WHERE (s.user_agent_id = $1) AND (s.user_id = $2) diff --git a/internal/user/repository/view/user_sessions_by_user_agent.sql b/internal/user/repository/view/user_sessions_by_user_agent.sql index cb2171c096..a4b5682a76 100644 --- a/internal/user/repository/view/user_sessions_by_user_agent.sql +++ b/internal/user/repository/view/user_sessions_by_user_agent.sql @@ -20,8 +20,8 @@ SELECT s.creation_date, s.instance_id, s.id FROM auth.user_sessions s - LEFT JOIN projections.users13 u ON s.user_id = u.id AND s.instance_id = u.instance_id - LEFT JOIN projections.users13_humans h ON s.user_id = h.user_id AND s.instance_id = h.instance_id + LEFT JOIN projections.users14 u ON s.user_id = u.id AND s.instance_id = u.instance_id + LEFT JOIN projections.users14_humans h ON s.user_id = h.user_id AND s.instance_id = h.instance_id LEFT JOIN projections.login_names3 l ON s.user_id = l.user_id AND s.instance_id = l.instance_id AND l.is_primary = true WHERE (s.user_agent_id = $1 and s.user_agent_id <> '') AND (s.instance_id = $2) diff --git a/proto/zitadel/user/v2/user.proto b/proto/zitadel/user/v2/user.proto index b569b81bbd..e2a140ea27 100644 --- a/proto/zitadel/user/v2/user.proto +++ b/proto/zitadel/user/v2/user.proto @@ -175,6 +175,8 @@ message HumanUser { bool password_change_required = 9; // The time the user last changed their password. google.protobuf.Timestamp password_changed = 10; + // The time the user last skipped MFA initialization. + google.protobuf.Timestamp mfa_init_skipped = 11; } message User { diff --git a/proto/zitadel/user/v2/user_service.proto b/proto/zitadel/user/v2/user_service.proto index 7e5b8a02e8..5457efd64e 100644 --- a/proto/zitadel/user/v2/user_service.proto +++ b/proto/zitadel/user/v2/user_service.proto @@ -1206,6 +1206,32 @@ service UserService { }; }; } + + // MFA Init Skipped + // + // Update the last time the user has skipped MFA initialization. The server timestamp is used. + rpc HumanMFAInitSkipped(HumanMFAInitSkippedRequest) returns (HumanMFAInitSkippedResponse) { + option (google.api.http) = { + post: "/v2/users/{user_id}/mfa_init_skipped" + body: "*" + }; + + option (zitadel.protoc_gen_zitadel.v2.options) = { + auth_option: { + permission: "authenticated" + } + }; + + option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_operation) = { + responses: { + key: "200" + value: { + description: "OK"; + } + }; + }; + } + } message AddHumanUserRequest{ @@ -2336,3 +2362,19 @@ message VerifyInviteCodeRequest { message VerifyInviteCodeResponse { zitadel.object.v2.Details details = 1; } + +message HumanMFAInitSkippedRequest { + string user_id = 1 [ + (validate.rules).string = {min_len: 1, max_len: 200}, + (google.api.field_behavior) = REQUIRED, + (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = { + min_length: 1; + max_length: 200; + example: "\"69629012906488334\""; + } + ]; +} + +message HumanMFAInitSkippedResponse { + zitadel.object.v2.Details details = 1; +}