2024-07-12 02:30:25 +02:00
|
|
|
from collections.abc import Collection, Iterable
|
|
|
|
from typing import Any
|
2013-04-23 18:51:17 +02:00
|
|
|
|
2024-04-17 05:28:33 +02:00
|
|
|
from django.db.models import Model, QuerySet
|
2023-07-12 19:13:17 +02:00
|
|
|
from django.utils.timezone import now as timezone_now
|
2020-06-11 00:54:34 +02:00
|
|
|
|
|
|
|
from zerver.lib.create_user import create_user_profile, get_display_email_address
|
2013-07-29 23:03:31 +02:00
|
|
|
from zerver.lib.initial_password import initial_password
|
2020-03-24 14:47:41 +01:00
|
|
|
from zerver.lib.streams import render_stream_description
|
2021-12-09 08:04:00 +01:00
|
|
|
from zerver.models import (
|
2024-04-02 18:39:18 +02:00
|
|
|
NamedUserGroup,
|
2021-12-09 08:04:00 +01:00
|
|
|
Realm,
|
|
|
|
RealmAuditLog,
|
|
|
|
RealmUserDefault,
|
|
|
|
Recipient,
|
|
|
|
Stream,
|
|
|
|
Subscription,
|
2022-07-12 13:42:29 +02:00
|
|
|
UserGroupMembership,
|
2021-12-09 08:04:00 +01:00
|
|
|
UserProfile,
|
|
|
|
)
|
2023-12-15 01:55:59 +01:00
|
|
|
from zerver.models.groups import SystemGroups
|
2024-08-30 18:15:41 +02:00
|
|
|
from zerver.models.realm_audit_logs import AuditLogEventType
|
2020-06-11 00:54:34 +02:00
|
|
|
|
2013-01-10 21:50:09 +01:00
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
def bulk_create_users(
|
|
|
|
realm: Realm,
|
2024-07-12 02:30:17 +02:00
|
|
|
users_raw: set[tuple[str, str, bool]],
|
2024-07-12 02:30:23 +02:00
|
|
|
bot_type: int | None = None,
|
|
|
|
bot_owner: UserProfile | None = None,
|
|
|
|
tos_version: str | None = None,
|
2021-02-12 08:19:30 +01:00
|
|
|
timezone: str = "",
|
|
|
|
) -> None:
|
2013-01-10 21:50:09 +01:00
|
|
|
"""
|
2013-04-01 16:57:50 +02:00
|
|
|
Creates and saves a UserProfile with the given email.
|
2013-01-10 21:50:09 +01:00
|
|
|
Has some code based off of UserManage.create_user, but doesn't .save()
|
|
|
|
"""
|
2021-02-12 08:19:30 +01:00
|
|
|
existing_users = frozenset(
|
2021-02-12 08:20:45 +01:00
|
|
|
UserProfile.objects.filter(realm=realm).values_list("email", flat=True)
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2020-09-02 06:20:26 +02:00
|
|
|
users = sorted(user_raw for user_raw in users_raw if user_raw[0] not in existing_users)
|
2021-10-26 09:15:16 +02:00
|
|
|
|
2021-12-09 08:04:00 +01:00
|
|
|
realm_user_default = RealmUserDefault.objects.get(realm=realm)
|
2021-10-26 09:15:16 +02:00
|
|
|
if bot_type is None:
|
|
|
|
email_address_visibility = realm_user_default.email_address_visibility
|
|
|
|
else:
|
|
|
|
# There is no privacy motivation for limiting access to bot email addresses,
|
|
|
|
# so we hardcode them to EMAIL_ADDRESS_VISIBILITY_EVERYONE.
|
|
|
|
email_address_visibility = UserProfile.EMAIL_ADDRESS_VISIBILITY_EVERYONE
|
2013-01-10 21:50:09 +01:00
|
|
|
|
|
|
|
# Now create user_profiles
|
2024-07-12 02:30:17 +02:00
|
|
|
profiles_to_create: list[UserProfile] = []
|
2023-02-02 04:35:24 +01:00
|
|
|
for email, full_name, active in users:
|
2021-02-12 08:19:30 +01:00
|
|
|
profile = create_user_profile(
|
|
|
|
realm,
|
|
|
|
email,
|
|
|
|
initial_password(email),
|
|
|
|
active,
|
|
|
|
bot_type,
|
|
|
|
full_name,
|
|
|
|
bot_owner,
|
|
|
|
False,
|
|
|
|
tos_version,
|
|
|
|
timezone,
|
2023-09-29 19:09:45 +02:00
|
|
|
default_language=realm.default_language,
|
2021-10-26 09:15:16 +02:00
|
|
|
email_address_visibility=email_address_visibility,
|
2021-02-12 08:19:30 +01:00
|
|
|
)
|
2021-12-09 08:04:00 +01:00
|
|
|
|
|
|
|
if bot_type is None:
|
|
|
|
# This block simulates copy_default_settings from
|
|
|
|
# zerver/lib/create_user.py.
|
|
|
|
#
|
|
|
|
# We cannot use 'copy_default_settings' directly here
|
|
|
|
# because it calls '.save' after copying the settings, and
|
|
|
|
# we are bulk creating the objects here instead.
|
|
|
|
for settings_name in RealmUserDefault.property_types:
|
|
|
|
if settings_name in ["default_language", "enable_login_emails"]:
|
|
|
|
continue
|
|
|
|
value = getattr(realm_user_default, settings_name)
|
|
|
|
setattr(profile, settings_name, value)
|
2013-01-10 21:50:09 +01:00
|
|
|
profiles_to_create.append(profile)
|
2020-03-06 17:58:06 +01:00
|
|
|
|
2021-10-26 09:15:16 +02:00
|
|
|
if email_address_visibility == UserProfile.EMAIL_ADDRESS_VISIBILITY_EVERYONE:
|
2020-03-06 17:58:06 +01:00
|
|
|
UserProfile.objects.bulk_create(profiles_to_create)
|
|
|
|
else:
|
|
|
|
for user_profile in profiles_to_create:
|
|
|
|
user_profile.email = user_profile.delivery_email
|
|
|
|
|
|
|
|
UserProfile.objects.bulk_create(profiles_to_create)
|
|
|
|
|
|
|
|
for user_profile in profiles_to_create:
|
2020-12-19 17:51:38 +01:00
|
|
|
user_profile.email = get_display_email_address(user_profile)
|
2021-02-12 08:20:45 +01:00
|
|
|
UserProfile.objects.bulk_update(profiles_to_create, ["email"])
|
2020-03-06 17:58:06 +01:00
|
|
|
|
2020-03-06 14:48:49 +01:00
|
|
|
user_ids = {user.id for user in profiles_to_create}
|
2013-01-10 21:50:09 +01:00
|
|
|
|
2017-02-15 04:35:10 +01:00
|
|
|
RealmAuditLog.objects.bulk_create(
|
2021-02-12 08:19:30 +01:00
|
|
|
RealmAuditLog(
|
|
|
|
realm=realm,
|
|
|
|
modified_user=profile_,
|
2024-08-30 18:15:41 +02:00
|
|
|
event_type=AuditLogEventType.USER_CREATED,
|
2021-02-12 08:19:30 +01:00
|
|
|
event_time=profile_.date_joined,
|
|
|
|
)
|
|
|
|
for profile_ in profiles_to_create
|
|
|
|
)
|
2017-02-15 04:35:10 +01:00
|
|
|
|
2023-07-31 22:52:35 +02:00
|
|
|
recipients_to_create = [
|
|
|
|
Recipient(type_id=user_id, type=Recipient.PERSONAL) for user_id in user_ids
|
|
|
|
]
|
2020-03-06 14:48:49 +01:00
|
|
|
|
2013-03-27 15:58:23 +01:00
|
|
|
Recipient.objects.bulk_create(recipients_to_create)
|
2013-01-10 21:50:09 +01:00
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
bulk_set_users_or_streams_recipient_fields(
|
|
|
|
UserProfile, profiles_to_create, recipients_to_create
|
|
|
|
)
|
2019-11-28 16:56:04 +01:00
|
|
|
|
2024-07-12 02:30:17 +02:00
|
|
|
recipients_by_user_id: dict[int, Recipient] = {}
|
2017-11-22 06:05:25 +01:00
|
|
|
for recipient in recipients_to_create:
|
2020-03-06 14:48:49 +01:00
|
|
|
recipients_by_user_id[recipient.type_id] = recipient
|
2013-01-10 21:50:09 +01:00
|
|
|
|
2023-07-31 22:52:35 +02:00
|
|
|
subscriptions_to_create = [
|
|
|
|
Subscription(
|
2021-02-14 00:03:40 +01:00
|
|
|
user_profile_id=user_profile.id,
|
2023-07-31 22:52:35 +02:00
|
|
|
recipient=recipients_by_user_id[user_profile.id],
|
2021-02-14 00:03:40 +01:00
|
|
|
is_user_active=user_profile.is_active,
|
|
|
|
)
|
2023-07-31 22:52:35 +02:00
|
|
|
for user_profile in profiles_to_create
|
|
|
|
]
|
2020-03-06 14:48:49 +01:00
|
|
|
|
2013-03-27 15:58:23 +01:00
|
|
|
Subscription.objects.bulk_create(subscriptions_to_create)
|
2013-01-10 21:50:09 +01:00
|
|
|
|
2024-04-02 18:39:18 +02:00
|
|
|
full_members_system_group = NamedUserGroup.objects.get(
|
2023-09-21 13:06:39 +02:00
|
|
|
name=SystemGroups.FULL_MEMBERS, realm=realm, is_system_group=True
|
2022-07-12 13:42:29 +02:00
|
|
|
)
|
2024-04-02 18:39:18 +02:00
|
|
|
members_system_group = NamedUserGroup.objects.get(
|
2023-09-21 13:06:39 +02:00
|
|
|
name=SystemGroups.MEMBERS, realm=realm, is_system_group=True
|
2022-07-12 13:42:29 +02:00
|
|
|
)
|
2024-07-12 02:30:17 +02:00
|
|
|
group_memberships_to_create: list[UserGroupMembership] = []
|
2022-07-12 13:42:29 +02:00
|
|
|
for user_profile in profiles_to_create:
|
|
|
|
# All users are members since this function is only used to create bots
|
|
|
|
# and test and development environment users.
|
|
|
|
assert user_profile.role == UserProfile.ROLE_MEMBER
|
|
|
|
group_memberships_to_create.append(
|
|
|
|
UserGroupMembership(user_profile=user_profile, user_group=members_system_group)
|
|
|
|
)
|
|
|
|
if not user_profile.is_provisional_member:
|
|
|
|
group_memberships_to_create.append(
|
|
|
|
UserGroupMembership(user_profile=user_profile, user_group=full_members_system_group)
|
|
|
|
)
|
|
|
|
|
|
|
|
UserGroupMembership.objects.bulk_create(group_memberships_to_create)
|
2023-07-12 19:13:17 +02:00
|
|
|
now = timezone_now()
|
|
|
|
RealmAuditLog.objects.bulk_create(
|
|
|
|
RealmAuditLog(
|
|
|
|
realm=realm,
|
|
|
|
modified_user=membership.user_profile,
|
2024-04-17 16:34:39 +02:00
|
|
|
modified_user_group=membership.user_group.named_user_group,
|
2024-09-06 17:06:03 +02:00
|
|
|
event_type=AuditLogEventType.USER_GROUP_DIRECT_USER_MEMBERSHIP_ADDED,
|
2023-07-12 19:13:17 +02:00
|
|
|
event_time=now,
|
|
|
|
acting_user=None,
|
|
|
|
)
|
|
|
|
for membership in group_memberships_to_create
|
|
|
|
)
|
2022-07-12 13:42:29 +02:00
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
|
|
|
|
def bulk_set_users_or_streams_recipient_fields(
|
2024-07-12 02:30:17 +02:00
|
|
|
model: type[Model],
|
2024-07-12 02:30:23 +02:00
|
|
|
objects: Collection[UserProfile]
|
|
|
|
| QuerySet[UserProfile]
|
|
|
|
| Collection[Stream]
|
|
|
|
| QuerySet[Stream],
|
|
|
|
recipients: Iterable[Recipient] | None = None,
|
2021-02-12 08:19:30 +01:00
|
|
|
) -> None:
|
2019-11-28 16:56:04 +01:00
|
|
|
assert model in [UserProfile, Stream]
|
|
|
|
for obj in objects:
|
|
|
|
assert isinstance(obj, model)
|
|
|
|
|
|
|
|
if model == UserProfile:
|
|
|
|
recipient_type = Recipient.PERSONAL
|
|
|
|
elif model == Stream:
|
|
|
|
recipient_type = Recipient.STREAM
|
|
|
|
|
|
|
|
if recipients is None:
|
|
|
|
object_ids = [obj.id for obj in objects]
|
|
|
|
recipients = Recipient.objects.filter(type=recipient_type, type_id__in=object_ids)
|
|
|
|
|
2020-04-09 21:51:58 +02:00
|
|
|
objects_dict = {obj.id: obj for obj in objects}
|
2019-11-28 16:56:04 +01:00
|
|
|
|
2020-08-03 19:55:57 +02:00
|
|
|
objects_to_update = set()
|
2019-11-28 16:56:04 +01:00
|
|
|
for recipient in recipients:
|
|
|
|
assert recipient.type == recipient_type
|
|
|
|
result = objects_dict.get(recipient.type_id)
|
|
|
|
if result is not None:
|
|
|
|
result.recipient = recipient
|
2020-08-03 19:55:57 +02:00
|
|
|
objects_to_update.add(result)
|
2023-09-05 20:25:23 +02:00
|
|
|
model._default_manager.bulk_update(objects_to_update, ["recipient"])
|
2019-11-28 16:56:04 +01:00
|
|
|
|
2021-02-12 08:19:30 +01:00
|
|
|
|
2020-03-28 01:25:56 +01:00
|
|
|
# This is only sed in populate_db, so doesn't really need tests
|
2024-07-12 02:30:17 +02:00
|
|
|
def bulk_create_streams(realm: Realm, stream_dict: dict[str, dict[str, Any]]) -> None: # nocoverage
|
2020-09-02 06:20:26 +02:00
|
|
|
existing_streams = {
|
2021-02-12 08:20:45 +01:00
|
|
|
name.lower() for name in Stream.objects.filter(realm=realm).values_list("name", flat=True)
|
2020-09-02 06:20:26 +02:00
|
|
|
}
|
2024-04-02 18:39:18 +02:00
|
|
|
administrators_user_group = NamedUserGroup.objects.get(
|
2023-09-21 13:06:39 +02:00
|
|
|
name=SystemGroups.ADMINISTRATORS, is_system_group=True, realm=realm
|
2022-07-13 20:44:28 +02:00
|
|
|
)
|
2024-07-12 02:30:17 +02:00
|
|
|
streams_to_create: list[Stream] = []
|
2016-12-08 00:02:21 +01:00
|
|
|
for name, options in stream_dict.items():
|
2021-02-12 08:20:45 +01:00
|
|
|
if "history_public_to_subscribers" not in options:
|
|
|
|
options["history_public_to_subscribers"] = (
|
2021-02-12 08:19:30 +01:00
|
|
|
not options.get("invite_only", False) and not realm.is_zephyr_mirror_realm
|
|
|
|
)
|
2016-12-15 16:57:21 +01:00
|
|
|
if name.lower() not in existing_streams:
|
2016-12-08 00:02:21 +01:00
|
|
|
streams_to_create.append(
|
|
|
|
Stream(
|
2018-05-02 17:46:18 +02:00
|
|
|
realm=realm,
|
|
|
|
name=name,
|
|
|
|
description=options["description"],
|
2022-10-29 20:52:47 +02:00
|
|
|
rendered_description=render_stream_description(options["description"], realm),
|
2018-05-16 21:34:43 +02:00
|
|
|
invite_only=options.get("invite_only", False),
|
2021-02-12 08:19:30 +01:00
|
|
|
stream_post_policy=options.get(
|
|
|
|
"stream_post_policy", Stream.STREAM_POST_POLICY_EVERYONE
|
|
|
|
),
|
2018-05-02 17:46:18 +02:00
|
|
|
history_public_to_subscribers=options["history_public_to_subscribers"],
|
2018-05-16 21:54:38 +02:00
|
|
|
is_web_public=options.get("is_web_public", False),
|
2017-10-08 21:16:51 +02:00
|
|
|
is_in_zephyr_realm=realm.is_zephyr_mirror_realm,
|
2022-07-13 20:44:28 +02:00
|
|
|
can_remove_subscribers_group=administrators_user_group,
|
2024-05-14 05:48:41 +02:00
|
|
|
creator=options.get("creator", None),
|
python: Use trailing commas consistently.
Automatically generated by the following script, based on the output
of lint with flake8-comma:
import re
import sys
last_filename = None
last_row = None
lines = []
for msg in sys.stdin:
m = re.match(
r"\x1b\[35mflake8 \|\x1b\[0m \x1b\[1;31m(.+):(\d+):(\d+): (\w+)", msg
)
if m:
filename, row_str, col_str, err = m.groups()
row, col = int(row_str), int(col_str)
if filename == last_filename:
assert last_row != row
else:
if last_filename is not None:
with open(last_filename, "w") as f:
f.writelines(lines)
with open(filename) as f:
lines = f.readlines()
last_filename = filename
last_row = row
line = lines[row - 1]
if err in ["C812", "C815"]:
lines[row - 1] = line[: col - 1] + "," + line[col - 1 :]
elif err in ["C819"]:
assert line[col - 2] == ","
lines[row - 1] = line[: col - 2] + line[col - 1 :].lstrip(" ")
if last_filename is not None:
with open(last_filename, "w") as f:
f.writelines(lines)
Signed-off-by: Anders Kaseorg <anders@zulipchat.com>
2020-04-10 05:23:40 +02:00
|
|
|
),
|
2016-12-08 00:02:21 +01:00
|
|
|
)
|
2017-07-16 22:46:34 +02:00
|
|
|
# Sort streams by name before creating them so that we can have a
|
|
|
|
# reliable ordering of `stream_id` across different python versions.
|
|
|
|
# This is required for test fixtures which contain `stream_id`. Prior
|
|
|
|
# to python 3.3 hashes were not randomized but after a security fix
|
|
|
|
# hash randomization was enabled in python 3.3 which made iteration
|
|
|
|
# of dictionaries and sets completely unpredictable. Here the order
|
|
|
|
# of elements while iterating `stream_dict` will be completely random
|
|
|
|
# for python 3.3 and later versions.
|
|
|
|
streams_to_create.sort(key=lambda x: x.name)
|
2013-03-27 15:58:23 +01:00
|
|
|
Stream.objects.bulk_create(streams_to_create)
|
2013-01-10 21:50:09 +01:00
|
|
|
|
2023-07-31 22:52:35 +02:00
|
|
|
recipients_to_create = [
|
|
|
|
Recipient(type_id=stream["id"], type=Recipient.STREAM)
|
|
|
|
for stream in Stream.objects.filter(realm=realm).values("id", "name")
|
|
|
|
if stream["name"].lower() not in existing_streams
|
|
|
|
]
|
2013-03-27 15:58:23 +01:00
|
|
|
Recipient.objects.bulk_create(recipients_to_create)
|
2019-11-28 16:56:04 +01:00
|
|
|
|
|
|
|
bulk_set_users_or_streams_recipient_fields(Stream, streams_to_create, recipients_to_create)
|
2022-04-14 23:46:18 +02:00
|
|
|
|
|
|
|
|
|
|
|
def create_users(
|
2024-07-12 02:30:23 +02:00
|
|
|
realm: Realm, name_list: Iterable[tuple[str, str]], bot_type: int | None = None
|
2022-04-14 23:46:18 +02:00
|
|
|
) -> None:
|
2024-07-14 21:17:13 +02:00
|
|
|
user_set = {(email, full_name, True) for full_name, email in name_list}
|
2022-04-14 23:46:18 +02:00
|
|
|
bulk_create_users(realm, user_set, bot_type)
|