From c978bfaa32e08098208761f371d4a9ec69b9ccaa Mon Sep 17 00:00:00 2001 From: Alex Vandiver Date: Thu, 18 May 2023 15:21:21 +0000 Subject: [PATCH] models: Add a unique index on UserProfile.api_key. This prevents `get_user_profile_by_api_key` from doing a sequential scan. Doing this requires moving the generation of initial api_key values into the column definition, so that even bare calls to `UserProfile.objects.create` (e.g. from tests) call appropriately generate a random initial value. --- tools/semgrep.yml | 1 + zerver/lib/create_user.py | 2 -- .../0451_add_userprofile_api_key_index.py | 20 +++++++++++++++++++ zerver/models.py | 4 ++-- 4 files changed, 23 insertions(+), 4 deletions(-) create mode 100644 zerver/migrations/0451_add_userprofile_api_key_index.py diff --git a/tools/semgrep.yml b/tools/semgrep.yml index 51bb15991c..975d1959d3 100644 --- a/tools/semgrep.yml +++ b/tools/semgrep.yml @@ -20,6 +20,7 @@ rules: - id: dont-import-models-in-migrations patterns: - pattern-not: from zerver.lib.redis_utils import get_redis_client + - pattern-not: from zerver.lib.utils import generate_api_key - pattern-not: from zerver.models import filter_pattern_validator - pattern-not: from zerver.models import url_template_validator - pattern-not: from zerver.models import generate_email_token_for_stream diff --git a/zerver/lib/create_user.py b/zerver/lib/create_user.py index d54f4bebd9..ba0cad4b9e 100644 --- a/zerver/lib/create_user.py +++ b/zerver/lib/create_user.py @@ -9,7 +9,6 @@ from django.utils.timezone import now as timezone_now from zerver.lib.hotspots import copy_hotspots from zerver.lib.timezone import canonicalize_timezone from zerver.lib.upload import copy_avatar -from zerver.lib.utils import generate_api_key from zerver.models import ( Realm, RealmUserDefault, @@ -137,7 +136,6 @@ def create_user_profile( # If emails are visible to everyone, we can set this here and save a DB query user_profile.email = get_display_email_address(user_profile) user_profile.set_password(password) - user_profile.api_key = generate_api_key() return user_profile diff --git a/zerver/migrations/0451_add_userprofile_api_key_index.py b/zerver/migrations/0451_add_userprofile_api_key_index.py new file mode 100644 index 0000000000..1864738d59 --- /dev/null +++ b/zerver/migrations/0451_add_userprofile_api_key_index.py @@ -0,0 +1,20 @@ +# Generated by Django 4.2.1 on 2023-05-18 15:20 + + +from django.db import migrations, models + +from zerver.lib.utils import generate_api_key + + +class Migration(migrations.Migration): + dependencies = [ + ("zerver", "0450_backfill_subscription_auditlogs"), + ] + + operations = [ + migrations.AlterField( + model_name="userprofile", + name="api_key", + field=models.CharField(default=generate_api_key, max_length=32, unique=True), + ), + ] diff --git a/zerver/models.py b/zerver/models.py index 25670875c3..3c98a4116a 100644 --- a/zerver/models.py +++ b/zerver/models.py @@ -101,7 +101,7 @@ from zerver.lib.types import ( UserFieldElement, Validator, ) -from zerver.lib.utils import make_safe_digest +from zerver.lib.utils import generate_api_key, make_safe_digest from zerver.lib.validator import ( check_date, check_int, @@ -1822,7 +1822,7 @@ class UserProfile(AbstractBaseUser, PermissionsMixin, UserBaseSettings): # type # fully created on servers that do not have a configured ToS. TOS_VERSION_BEFORE_FIRST_LOGIN = "-1" tos_version = models.CharField(null=True, max_length=10) - api_key = models.CharField(max_length=API_KEY_LENGTH) + api_key = models.CharField(max_length=API_KEY_LENGTH, default=generate_api_key, unique=True) # A UUID generated on user creation. Introduced primarily to # provide a unique key for a user for the mobile push