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.
This commit is contained in:
Alex Vandiver 2023-05-18 15:21:21 +00:00 committed by Tim Abbott
parent 27bc36b7d6
commit c978bfaa32
4 changed files with 23 additions and 4 deletions

View File

@ -20,6 +20,7 @@ rules:
- id: dont-import-models-in-migrations - id: dont-import-models-in-migrations
patterns: patterns:
- pattern-not: from zerver.lib.redis_utils import get_redis_client - 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 filter_pattern_validator
- pattern-not: from zerver.models import url_template_validator - pattern-not: from zerver.models import url_template_validator
- pattern-not: from zerver.models import generate_email_token_for_stream - pattern-not: from zerver.models import generate_email_token_for_stream

View File

@ -9,7 +9,6 @@ from django.utils.timezone import now as timezone_now
from zerver.lib.hotspots import copy_hotspots from zerver.lib.hotspots import copy_hotspots
from zerver.lib.timezone import canonicalize_timezone from zerver.lib.timezone import canonicalize_timezone
from zerver.lib.upload import copy_avatar from zerver.lib.upload import copy_avatar
from zerver.lib.utils import generate_api_key
from zerver.models import ( from zerver.models import (
Realm, Realm,
RealmUserDefault, 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 # 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.email = get_display_email_address(user_profile)
user_profile.set_password(password) user_profile.set_password(password)
user_profile.api_key = generate_api_key()
return user_profile return user_profile

View File

@ -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),
),
]

View File

@ -101,7 +101,7 @@ from zerver.lib.types import (
UserFieldElement, UserFieldElement,
Validator, 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 ( from zerver.lib.validator import (
check_date, check_date,
check_int, check_int,
@ -1822,7 +1822,7 @@ class UserProfile(AbstractBaseUser, PermissionsMixin, UserBaseSettings): # type
# fully created on servers that do not have a configured ToS. # fully created on servers that do not have a configured ToS.
TOS_VERSION_BEFORE_FIRST_LOGIN = "-1" TOS_VERSION_BEFORE_FIRST_LOGIN = "-1"
tos_version = models.CharField(null=True, max_length=10) 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 # A UUID generated on user creation. Introduced primarily to
# provide a unique key for a user for the mobile push # provide a unique key for a user for the mobile push