diff --git a/CHANGELOG.md b/CHANGELOG.md index 432303eda..35f284725 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,10 @@ and this project adheres to ## [Unreleased] +### Added + +- ⚡️(backend) add UPPER(email) index for case-insensitive user lookups + ### Changed - ⚡️(backend) hash application secrets with SHA-256 diff --git a/src/backend/core/migrations/0026_user_email_upper_idx.py b/src/backend/core/migrations/0026_user_email_upper_idx.py new file mode 100644 index 000000000..c778723f9 --- /dev/null +++ b/src/backend/core/migrations/0026_user_email_upper_idx.py @@ -0,0 +1,23 @@ +# Generated by Django 5.2.14 on 2026-10-07 18:45 + +from django.contrib.postgres.operations import AddIndexConcurrently +from django.db import migrations, models +from django.db.models.functions import Upper + + +class Migration(migrations.Migration): + atomic = False + + dependencies = [ + ('core', '0025_application_client_secret_sha256'), + ] + + operations = [ + AddIndexConcurrently( + model_name="user", + index=models.Index( + Upper("email"), + name="user_email_upper_idx", + ), + ), + ] diff --git a/src/backend/core/models.py b/src/backend/core/models.py index 8d4d13a3e..284ff98a0 100644 --- a/src/backend/core/models.py +++ b/src/backend/core/models.py @@ -246,6 +246,9 @@ class User(AbstractBaseUser, BaseModel, auth_models.PermissionsMixin): name="unique_email_when_sub_is_null", ) ] + indexes = [ + models.Index(models.functions.Upper("email"), name="user_email_upper_idx"), + ] def __str__(self): return self.email or self.admin_email or str(self.id)