diff --git a/cmd/setup/25.go b/cmd/setup/25.go new file mode 100644 index 0000000000..7a6ac6eccf --- /dev/null +++ b/cmd/setup/25.go @@ -0,0 +1,27 @@ +package setup + +import ( + "context" + _ "embed" + + "github.com/zitadel/zitadel/internal/database" + "github.com/zitadel/zitadel/internal/eventstore" +) + +var ( + //go:embed 25.sql + addLowerFieldsToVerifiedEmail string +) + +type AddLowerFieldsToVerifiedEmail struct { + dbClient *database.DB +} + +func (mig *AddLowerFieldsToVerifiedEmail) Execute(ctx context.Context, _ eventstore.Event) error { + _, err := mig.dbClient.ExecContext(ctx, addLowerFieldsToVerifiedEmail) + return err +} + +func (mig *AddLowerFieldsToVerifiedEmail) String() string { + return "25_add_lower_fields_to_verified_email" +} diff --git a/cmd/setup/25.sql b/cmd/setup/25.sql new file mode 100644 index 0000000000..59db819847 --- /dev/null +++ b/cmd/setup/25.sql @@ -0,0 +1,2 @@ +ALTER TABLE IF EXISTS projections.users10_notifications ADD COLUMN IF NOT EXISTS verified_email_lower TEXT GENERATED ALWAYS AS (lower(verified_email)) STORED; +CREATE INDEX IF NOT EXISTS users10_notifications_email_search ON projections.users10_notifications (instance_id, verified_email_lower); diff --git a/cmd/setup/config.go b/cmd/setup/config.go index 59143d87dd..510999bcce 100644 --- a/cmd/setup/config.go +++ b/cmd/setup/config.go @@ -103,6 +103,7 @@ type Steps struct { s22ActiveInstancesIndex *ActiveInstanceEvents s23CorrectGlobalUniqueConstraints *CorrectGlobalUniqueConstraints s24AddActorToAuthTokens *AddActorToAuthTokens + s25AddLowerFieldsToVerifiedEmail *AddLowerFieldsToVerifiedEmail } func MustNewSteps(v *viper.Viper) *Steps { diff --git a/cmd/setup/setup.go b/cmd/setup/setup.go index a493b2569f..16d7b62a7d 100644 --- a/cmd/setup/setup.go +++ b/cmd/setup/setup.go @@ -137,6 +137,7 @@ func Setup(config *Config, steps *Steps, masterKey string) { steps.s22ActiveInstancesIndex = &ActiveInstanceEvents{dbClient: queryDBClient} steps.s23CorrectGlobalUniqueConstraints = &CorrectGlobalUniqueConstraints{dbClient: esPusherDBClient} steps.s24AddActorToAuthTokens = &AddActorToAuthTokens{dbClient: queryDBClient} + steps.s25AddLowerFieldsToVerifiedEmail = &AddLowerFieldsToVerifiedEmail{dbClient: esPusherDBClient} err = projection.Create(ctx, projectionDBClient, eventstoreClient, config.Projections, nil, nil, nil) logging.OnError(err).Fatal("unable to start projections") @@ -186,6 +187,7 @@ func Setup(config *Config, steps *Steps, masterKey string) { for _, step := range []migration.Migration{ steps.s18AddLowerFieldsToLoginNames, steps.s21AddBlockFieldToLimits, + steps.s25AddLowerFieldsToVerifiedEmail, } { mustExecuteMigration(ctx, eventstoreClient, step, "migration failed") } diff --git a/internal/auth/repository/eventsourcing/view/user.go b/internal/auth/repository/eventsourcing/view/user.go index 8479924f9a..3c1c3ddcc1 100644 --- a/internal/auth/repository/eventsourcing/view/user.go +++ b/internal/auth/repository/eventsourcing/view/user.go @@ -50,7 +50,7 @@ func (v *View) UserByLoginNameAndResourceOwner(ctx context.Context, loginName, r } func (v *View) UserByEmail(ctx context.Context, email, instanceID string) (*model.UserView, error) { - emailQuery, err := query.NewUserVerifiedEmailSearchQuery(email, query.TextEqualsIgnoreCase) + emailQuery, err := query.NewUserVerifiedEmailSearchQuery(email) if err != nil { return nil, err } @@ -58,7 +58,7 @@ func (v *View) UserByEmail(ctx context.Context, email, instanceID string) (*mode } func (v *View) UserByEmailAndResourceOwner(ctx context.Context, email, resourceOwner, instanceID string) (*model.UserView, error) { - emailQuery, err := query.NewUserVerifiedEmailSearchQuery(email, query.TextEquals) + emailQuery, err := query.NewUserVerifiedEmailSearchQuery(email) if err != nil { return nil, err } diff --git a/internal/query/projection/user.go b/internal/query/projection/user.go index 61a9708546..ab1c158bb9 100644 --- a/internal/query/projection/user.go +++ b/internal/query/projection/user.go @@ -61,14 +61,15 @@ const ( MachineAccessTokenTypeCol = "access_token_type" // notify - UserNotifySuffix = "notifications" - NotifyUserIDCol = "user_id" - NotifyInstanceIDCol = "instance_id" - NotifyLastEmailCol = "last_email" - NotifyVerifiedEmailCol = "verified_email" - NotifyLastPhoneCol = "last_phone" - NotifyVerifiedPhoneCol = "verified_phone" - NotifyPasswordSetCol = "password_set" + UserNotifySuffix = "notifications" + NotifyUserIDCol = "user_id" + NotifyInstanceIDCol = "instance_id" + NotifyLastEmailCol = "last_email" + NotifyVerifiedEmailCol = "verified_email" + NotifyVerifiedEmailLowerCol = "verified_email_lower" + NotifyLastPhoneCol = "last_phone" + NotifyVerifiedPhoneCol = "verified_phone" + NotifyPasswordSetCol = "password_set" ) type userProjection struct{} diff --git a/internal/query/user.go b/internal/query/user.go index 5209f1b08c..9367dca35b 100644 --- a/internal/query/user.go +++ b/internal/query/user.go @@ -324,6 +324,10 @@ var ( table: notifyTable, isOrderByLower: true, } + NotifyVerifiedEmailLowerCaseCol = Column{ + name: projection.NotifyVerifiedEmailLowerCol, + table: notifyTable, + } NotifyPhoneCol = Column{ name: projection.NotifyLastPhoneCol, table: notifyTable, @@ -714,8 +718,8 @@ func NewUserPhoneSearchQuery(value string, comparison TextComparison) (SearchQue return NewTextQuery(HumanPhoneCol, value, comparison) } -func NewUserVerifiedEmailSearchQuery(value string, comparison TextComparison) (SearchQuery, error) { - return NewTextQuery(NotifyVerifiedEmailCol, value, comparison) +func NewUserVerifiedEmailSearchQuery(value string) (SearchQuery, error) { + return NewTextQuery(NotifyVerifiedEmailLowerCaseCol, strings.ToLower(value), TextEquals) } func NewUserVerifiedPhoneSearchQuery(value string, comparison TextComparison) (SearchQuery, error) {