mirror of https://github.com/zulip/zulip.git
realm: Add uuid and associated secret columns.
Thisi and the following commit follow the approach used in
3e2ad84bbe
.
First migration requires a server restart - after that any new realms
will be created with the columns set.
The following migrations are in the next commit:
Second migration does a backfill for older realms and can run in the
background while the server is operating normally.
Third migration enforces null=False now that all realms have the columns
set.
This commit is contained in:
parent
23f53752b2
commit
3afe585922
|
@ -0,0 +1,22 @@
|
|||
# Generated by Django 4.2.5 on 2023-10-06 09:21
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
("zerver", "0478_realm_enable_guest_user_indicator"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="realm",
|
||||
name="uuid",
|
||||
field=models.UUIDField(null=True, unique=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="realm",
|
||||
name="uuid_owner_secret",
|
||||
field=models.TextField(null=True),
|
||||
),
|
||||
]
|
|
@ -275,6 +275,14 @@ class RealmAuthenticationMethod(models.Model):
|
|||
unique_together = ("realm", "name")
|
||||
|
||||
|
||||
def generate_realm_uuid_owner_secret() -> str:
|
||||
token = generate_api_key()
|
||||
|
||||
# We include a prefix to facilitate scanning for accidental
|
||||
# disclosure of secrets e.g. in Github commit pushes.
|
||||
return f"zuliprealm_{token}"
|
||||
|
||||
|
||||
class Realm(models.Model): # type: ignore[django-manager-missing] # django-stubs cannot resolve the custom CTEManager yet https://github.com/typeddjango/django-stubs/issues/1023
|
||||
MAX_REALM_NAME_LENGTH = 40
|
||||
MAX_REALM_DESCRIPTION_LENGTH = 1000
|
||||
|
@ -295,6 +303,11 @@ class Realm(models.Model): # type: ignore[django-manager-missing] # django-stub
|
|||
# at `foo.example.com`.
|
||||
string_id = models.CharField(max_length=MAX_REALM_SUBDOMAIN_LENGTH, unique=True)
|
||||
|
||||
# uuid and a secret for the sake of per-realm authentication with the push notification
|
||||
# bouncer.
|
||||
uuid = models.UUIDField(default=uuid4, unique=True)
|
||||
uuid_owner_secret = models.TextField(default=generate_realm_uuid_owner_secret)
|
||||
|
||||
date_created = models.DateTimeField(default=timezone_now)
|
||||
demo_organization_scheduled_deletion_date = models.DateTimeField(default=None, null=True)
|
||||
deactivated = models.BooleanField(default=False)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import datetime
|
||||
import os
|
||||
import shutil
|
||||
import uuid
|
||||
from collections import defaultdict
|
||||
from typing import Any, Callable, Dict, FrozenSet, Iterable, List, Optional, Set, Tuple
|
||||
from unittest.mock import patch
|
||||
|
@ -329,6 +330,13 @@ class RealmImportExportTest(ExportFile):
|
|||
consent_message_id=consent_message_id,
|
||||
public_only=public_only,
|
||||
)
|
||||
|
||||
# This is a unique field and thus the cycle of export->import
|
||||
# within the same server (which is what happens in our tests)
|
||||
# will cause a conflict - so rotate it.
|
||||
realm.uuid = uuid.uuid4()
|
||||
realm.save()
|
||||
|
||||
export_usermessages_batch(
|
||||
input_path=os.path.join(output_dir, "messages-000001.json.partial"),
|
||||
output_path=os.path.join(output_dir, "messages-000001.json"),
|
||||
|
@ -1538,25 +1546,31 @@ class RealmImportExportTest(ExportFile):
|
|||
self.export_realm(realm)
|
||||
|
||||
with self.settings(BILLING_ENABLED=True), self.assertLogs(level="INFO"):
|
||||
realm = do_import_realm(get_output_dir(), "test-zulip-1")
|
||||
self.assertEqual(realm.plan_type, Realm.PLAN_TYPE_LIMITED)
|
||||
self.assertEqual(realm.max_invites, 100)
|
||||
self.assertEqual(realm.upload_quota_gb, 5)
|
||||
self.assertEqual(realm.message_visibility_limit, 10000)
|
||||
imported_realm = do_import_realm(get_output_dir(), "test-zulip-1")
|
||||
self.assertEqual(imported_realm.plan_type, Realm.PLAN_TYPE_LIMITED)
|
||||
self.assertEqual(imported_realm.max_invites, 100)
|
||||
self.assertEqual(imported_realm.upload_quota_gb, 5)
|
||||
self.assertEqual(imported_realm.message_visibility_limit, 10000)
|
||||
self.assertTrue(
|
||||
RealmAuditLog.objects.filter(
|
||||
realm=realm, event_type=RealmAuditLog.REALM_PLAN_TYPE_CHANGED
|
||||
realm=imported_realm, event_type=RealmAuditLog.REALM_PLAN_TYPE_CHANGED
|
||||
).exists()
|
||||
)
|
||||
|
||||
# Importing the same export data twice would cause conflict on unique fields,
|
||||
# so instead re-export the original realm via self.export_realm, which handles
|
||||
# this issue.
|
||||
self.export_realm(realm)
|
||||
|
||||
with self.settings(BILLING_ENABLED=False), self.assertLogs(level="INFO"):
|
||||
realm = do_import_realm(get_output_dir(), "test-zulip-2")
|
||||
self.assertEqual(realm.plan_type, Realm.PLAN_TYPE_SELF_HOSTED)
|
||||
self.assertEqual(realm.max_invites, 100)
|
||||
self.assertEqual(realm.upload_quota_gb, None)
|
||||
self.assertEqual(realm.message_visibility_limit, None)
|
||||
imported_realm = do_import_realm(get_output_dir(), "test-zulip-2")
|
||||
self.assertEqual(imported_realm.plan_type, Realm.PLAN_TYPE_SELF_HOSTED)
|
||||
self.assertEqual(imported_realm.max_invites, 100)
|
||||
self.assertEqual(imported_realm.upload_quota_gb, None)
|
||||
self.assertEqual(imported_realm.message_visibility_limit, None)
|
||||
self.assertTrue(
|
||||
RealmAuditLog.objects.filter(
|
||||
realm=realm, event_type=RealmAuditLog.REALM_PLAN_TYPE_CHANGED
|
||||
realm=imported_realm, event_type=RealmAuditLog.REALM_PLAN_TYPE_CHANGED
|
||||
).exists()
|
||||
)
|
||||
|
||||
|
|
Loading…
Reference in New Issue