fix: allow login by email case-insensitive (#7578)

A customer noted that the login by email was case-sensitive, which differs to the handling of the loginname.

This PR changes the email check to be case-insensitive (which it was already in same parts) and improve the search for this as well.
This commit is contained in:
Livio Spring 2024-03-20 16:51:26 +01:00 committed by GitHub
parent b2d7352a5a
commit 7e24a1adbc
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 49 additions and 12 deletions

27
cmd/setup/25.go Normal file
View File

@ -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"
}

2
cmd/setup/25.sql Normal file
View File

@ -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);

View File

@ -103,6 +103,7 @@ type Steps struct {
s22ActiveInstancesIndex *ActiveInstanceEvents
s23CorrectGlobalUniqueConstraints *CorrectGlobalUniqueConstraints
s24AddActorToAuthTokens *AddActorToAuthTokens
s25AddLowerFieldsToVerifiedEmail *AddLowerFieldsToVerifiedEmail
}
func MustNewSteps(v *viper.Viper) *Steps {

View File

@ -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")
}

View File

@ -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
}

View File

@ -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{}

View File

@ -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) {