zulip/zerver/tests
Zixuan James Li a081428ad2 user_groups: Make locks required for updating user group memberships.
**Background**

User groups are expected to comply with the DAG constraint for the
many-to-many inter-group membership. The check for this constraint has
to be performed recursively so that we can find all direct and indirect
subgroups of the user group to be added.

This kind of check is vulnerable to phantom reads which is possible at
the default read committed isolation level because we cannot guarantee
that the check is still valid when we are adding the subgroups to the
user group.

**Solution**

To avoid having another transaction concurrently update one of the
to-be-subgroup after the recursive check is done, and before the subgroup
is added, we use SELECT FOR UPDATE to lock the user group rows.

The lock needs to be acquired before a group membership change is about
to occur before any check has been conducted.

Suppose that we are adding subgroup B to supergroup A, the locking protocol
is specified as follows:

1. Acquire a lock for B and all its direct and indirect subgroups.
2. Acquire a lock for A.

For the removal of user groups, we acquire a lock for the user group to
be removed with all its direct and indirect subgroups. This is the special
case A=B, which is still complaint with the protocol.

**Error handling**

We currently rely on Postgres' deadlock detection to abort transactions
and show an error for the users. In the future, we might need some
recovery mechanism or at least better error handling.

**Notes**

An important note is that we need to reuse the recursive CTE query that
finds the direct and indirect subgroups when applying the lock on the
rows. And the lock needs to be acquired the same way for the addition and
removal of direct subgroups.

User membership change (as opposed to user group membership) is not
affected. Read-only queries aren't either. The locks only protect
critical regions where the user group dependency graph might violate
the DAG constraint, where users are not participating.

**Testing**

We implement a transaction test case targeting some typical scenarios
when an internal server error is expected to happen (this means that the
user group view makes the correct decision to abort the transaction when
something goes wrong with locks).

To achieve this, we add a development view intended only for unit tests.
It has a global BARRIER that can be shared across threads, so that we
can synchronize them to consistently reproduce certain potential race
conditions prevented by the database locks.

The transaction test case lanuches pairs of threads initiating possibly
conflicting requests at the same time. The tests are set up such that exactly N
of them are expected to succeed with a certain error message (while we don't
know each one).

**Security notes**

get_recursive_subgroups_for_groups will no longer fetch user groups from
other realms. As a result, trying to add/remove a subgroup from another
realm results in a UserGroup not found error response.

We also implement subgroup-specific checks in has_user_group_access to
keep permission managing in a single place. Do note that the API
currently don't have a way to violate that check because we are only
checking the realm ID now.
2023-08-24 17:21:08 -07:00
..
fixtures emoji: Match emoji sequences in markdown. 2023-08-23 16:18:15 -07:00
images emoji: Support animated PNGs. 2022-03-15 12:47:21 -07:00
__init__.py
test_alert_words.py tests: Refactor away result.json() calls with helpers. 2022-06-06 23:06:00 -07:00
test_attachments.py upload: Rename delete_message_image to use word "attachment". 2023-03-02 16:36:19 -08:00
test_audit_log.py migration: Rename extra_data_json to extra_data in audit log models. 2023-08-16 17:18:14 -07:00
test_auth_backends.py ruff: Fix PERF401 Use a list comprehension to create a transformed list. 2023-08-07 17:23:55 -07:00
test_bots.py bots: Remove subscription from inaccessible streams on reactivating bot. 2023-08-16 15:37:37 -07:00
test_cache.py ruff: Fix N818 exception name should be named with an Error suffix. 2022-11-17 16:52:00 -08:00
test_compatibility.py ruff: Fix RSE102 Unnecessary parentheses on raised exception. 2023-02-04 16:34:55 -08:00
test_create_video_call.py tests: Refactor away result.json() calls with helpers. 2022-06-06 23:06:00 -07:00
test_custom_profile_data.py settings: Add backend code for using user email_address_visibility setting. 2023-02-10 17:35:49 -08:00
test_data_types.py
test_decorators.py requests: Split out test_has_request_variables. 2023-08-11 16:43:12 -07:00
test_delete_unclaimed_attachments.py lint: Fix lint issues caught after rebasing. 2023-08-06 13:47:38 -07:00
test_digest.py tests: Flush per-request caches automatically for query counts. 2023-08-11 11:09:34 -07:00
test_docs.py help: Add gear menu icons to relative links. 2023-08-07 11:40:44 -07:00
test_drafts.py backend-tests: Update "private message" or "PM" to "direct message". 2023-06-23 11:24:13 -07:00
test_email_change.py settings: Add backend code for using user email_address_visibility setting. 2023-02-10 17:35:49 -08:00
test_email_log.py emails: Add new onboarding email with guide for organization type. 2023-04-10 08:38:09 -07:00
test_email_mirror.py ruff: Fix PLW1510 subprocess.run without explicit check argument. 2023-08-17 17:05:34 -07:00
test_email_notifications.py email-templates: Add zulip_onboarding_topics email templates. 2023-08-18 16:25:48 -07:00
test_embedded_bot_system.py tests: Add assert_message_stream_name 2023-08-10 18:13:43 -07:00
test_event_queue.py notifications: Rename 'pm' to 'dm' in 'RecipientInfoResult' dataclass. 2023-08-10 17:41:49 -07:00
test_event_system.py api-docs: Fix documentation of realm_emoji in register response. 2023-08-16 14:32:20 -07:00
test_events.py user_groups: Make locks required for updating user group memberships. 2023-08-24 17:21:08 -07:00
test_example.py tests: Add assert_message_stream_name 2023-08-10 18:13:43 -07:00
test_external.py name_restrictions: Reject anything with zulip or kandra in it. 2023-06-23 10:45:40 -07:00
test_github.py ruff: Fix N818 exception name should be named with an Error suffix. 2022-11-17 16:52:00 -08:00
test_gitter_importer.py ruff: Fix RUF015 Prefer next(...) over single element slice. 2023-07-23 15:20:53 -07:00
test_has_request_variables.py requests: Split out test_has_request_variables. 2023-08-11 16:43:12 -07:00
test_home.py typing: Add typing constants to the post register api response. 2023-08-23 16:36:44 -07:00
test_hotspots.py actions: Split out zerver.actions.create_user. 2022-04-14 17:14:35 -07:00
test_i18n.py email-templates: Add account_registered email templates. 2023-08-18 16:25:48 -07:00
test_import_export.py test_import_export: Fix incorrect type annotation. 2023-08-17 17:05:34 -07:00
test_integrations_dev_panel.py black: Reformat with Black 23. 2023-02-02 10:40:13 -08:00
test_integrations.py integrations: Fix broken screenshots configuration. 2023-07-17 09:23:01 -07:00
test_internet.py ruff: Fix B017 assertRaises(Exception): should be considered evil. 2022-11-03 12:10:15 -07:00
test_invite.py ruff: Fix PERF401 Use a list comprehension to create a transformed list. 2023-08-07 17:23:55 -07:00
test_legacy_subject.py tests: Consistently JSON-encode ‘to’ parameter 2022-09-13 11:05:37 -07:00
test_link_embed.py message: Access realm object directly from message. 2023-08-23 11:38:32 -07:00
test_management_commands.py send_custom_email: Stop turning every user query into an id-based set. 2023-08-09 15:49:49 -07:00
test_markdown.py emoji: Match emoji sequences in markdown. 2023-08-23 16:18:15 -07:00
test_mattermost_importer.py mattermost: Update references to "private message" and "PM". 2023-06-23 11:24:13 -07:00
test_message_dict.py per-request caches: Add per_request_cache library. 2023-08-11 11:09:34 -07:00
test_message_edit_notifications.py notifications: Remove the stray notification trigger strings. 2023-07-24 11:02:14 -07:00
test_message_edit.py message_edit: Fix wildcard_mentioned flag set for all user-messages. 2023-08-16 11:31:56 -07:00
test_message_fetch.py Revert "narrow: Fix topic highlighting issue with apostrophes in search results." 2023-08-15 17:51:03 -07:00
test_message_flags.py bots: Remove private stream subscriptions on changing bot owner. 2023-08-16 15:37:37 -07:00
test_message_notification_emails.py notification_trigger: Rename private_message to direct_message. 2023-08-10 17:41:49 -07:00
test_message_send.py message_send: Fix wildcard_mentioned flag unset for few participants. 2023-08-16 11:31:56 -07:00
test_message_topics.py ruff: Fix N818 exception name should be named with an Error suffix. 2022-11-17 16:52:00 -08:00
test_messages.py notifications: Rename 'pm' to 'dm' in 'RecipientInfoResult' dataclass. 2023-08-10 17:41:49 -07:00
test_middleware.py codespell: Correct “requestor” to “requester”. 2023-06-20 16:17:55 -07:00
test_migrations.py scheduled-mails: Migrate existing scheduled emails to new templates. 2023-08-18 16:51:46 -07:00
test_mirror_users.py backend-tests: Update "private message" or "PM" to "direct message". 2023-06-23 11:24:13 -07:00
test_muted_users.py migration: Rename extra_data_json to extra_data in audit log models. 2023-08-16 17:18:14 -07:00
test_new_users.py create-user: Remove notifications sent to admin realm. 2023-03-13 12:28:26 -07:00
test_notification_data.py notifications: Rename 'pm' to 'dm' in 'RecipientInfoResult' dataclass. 2023-08-10 17:41:49 -07:00
test_onboarding.py
test_openapi.py test-openapi: Add coverage for documented fetch API key endpoints. 2023-08-15 09:52:55 -07:00
test_outgoing_http.py test: Replace occurences of uri with url. 2023-04-08 16:27:55 -07:00
test_outgoing_webhook_interfaces.py notification_trigger: Rename private_message to direct_message. 2023-08-10 17:41:49 -07:00
test_outgoing_webhook_system.py tests: Add assert_message_stream_name 2023-08-10 18:13:43 -07:00
test_populate_db.py populate_db: Import timedelta from its canonical module. 2023-03-05 14:46:28 -08:00
test_presence.py presence: Support null values in UserPresence. 2023-04-26 14:26:47 -07:00
test_push_notifications.py migration: Rename extra_data_json to extra_data in audit log models. 2023-08-16 17:18:14 -07:00
test_queue_worker.py notification_trigger: Rename private_message to direct_message. 2023-08-10 17:41:49 -07:00
test_queue.py test_queue_error_json: Acknowledge the received message. 2022-01-21 13:38:13 -08:00
test_rate_limiter.py test_helpers: Switch add/remove_ratelimit to a contextmanager. 2023-06-12 12:55:27 -07:00
test_reactions.py emojis: Use get_emoji_data. 2023-07-17 09:35:53 -07:00
test_read_receipts.py muted users: Make file naming consistent. 2023-02-10 15:39:57 -08:00
test_realm_domains.py realm_domains: Allow only owners to add, edit or delete domains. 2022-09-16 15:27:52 -07:00
test_realm_emoji.py docs: Add documentation for delete emoji endpoint. 2023-06-28 10:23:47 -07:00
test_realm_export.py migration: Rename extra_data_json to extra_data in audit log models. 2023-08-16 17:18:14 -07:00
test_realm_linkifiers.py migration: Rename extra_data_json to extra_data in audit log models. 2023-08-16 17:18:14 -07:00
test_realm_playgrounds.py realm_playgrounds: Refactor error handling for validation on creation. 2023-07-24 17:40:59 -07:00
test_realm.py email-templates: Add zulip_onboarding_topics email templates. 2023-08-18 16:25:48 -07:00
test_redis_utils.py typing: Fix missing attribtute of RedisUtilsTest. 2022-06-23 22:05:12 -07:00
test_report.py zerver: Remove now-unused report/ endpoints. 2023-05-09 13:16:28 -07:00
test_retention.py retention: Do not archive attachments with scheduled messages. 2023-08-06 13:40:02 -07:00
test_rocketchat_importer.py backend-tests: Update "private message" or "PM" to "direct message". 2023-06-23 11:24:13 -07:00
test_scheduled_messages.py tests: Access realm directly from ScheduledMessage object. 2023-08-23 11:38:32 -07:00
test_scim.py codespell: Correct “requestor” to “requester”. 2023-06-20 16:17:55 -07:00
test_send_email.py docs: Document how to use SMTP without authentication. 2023-02-27 11:59:48 -08:00
test_server_settings.py settings: Make SHARED_SECRET mandatory. 2022-08-25 12:13:03 -07:00
test_service_bot_system.py notification_trigger: Rename private_message to direct_message. 2023-08-10 17:41:49 -07:00
test_sessions.py black: Reformat with Black 23. 2023-02-02 10:40:13 -08:00
test_settings.py test_helpers: Switch add/remove_ratelimit to a contextmanager. 2023-06-12 12:55:27 -07:00
test_signup.py message: Access realm object directly from message. 2023-08-23 11:38:32 -07:00
test_slack_importer.py ruff: Fix PERF102 Using only the keys/values of a dict. 2023-08-07 17:23:55 -07:00
test_slack_message_conversion.py tests: Consistently use assert_length helper. 2021-05-19 11:55:56 -07:00
test_soft_deactivation.py ruff: Fix PERF401 Use a list comprehension to create a transformed list. 2023-08-07 17:23:55 -07:00
test_subdomains.py subdomains: Extend tests for for same-host when STATIC_URL has domain. 2023-02-14 17:17:06 -05:00
test_submessage.py tests: Improve automated tests for submessages. 2023-07-06 16:35:46 -07:00
test_subs.py message: Access realm object directly from message. 2023-08-23 11:38:32 -07:00
test_templates.py widgets: Rename confusing variable name in tabbed_instructions.ts. 2023-06-24 07:47:25 -07:00
test_thumbnail.py test_helpers: Switch add/remove_ratelimit to a contextmanager. 2023-06-12 12:55:27 -07:00
test_timeout.py test_timeout: Skip test_timeout_warn on Python 3.11 for coverage issue. 2023-05-18 11:52:22 -07:00
test_timestamp.py ruff: Fix N818 exception name should be named with an Error suffix. 2022-11-17 16:52:00 -08:00
test_timezone.py ruff: Fix DTZ001 datetime.datetime() without tzinfo argument. 2023-01-04 16:25:07 -08:00
test_tornado.py test_tornado: Avoid deprecated AsyncHTTPTestCase. 2022-07-05 17:54:17 -07:00
test_transfer.py upload: Rename upload_message_file to use word "attachment". 2023-03-02 16:36:19 -08:00
test_tutorial.py settings: Change "Display settings" to "Preferences". 2023-07-12 07:09:03 -07:00
test_typing.py message-type: Add support for "direct" as value for type parameter. 2023-04-18 12:29:33 -07:00
test_upload_local.py ruff: Collapse short multi-line import statements. 2023-08-02 17:41:41 -07:00
test_upload_s3.py ruff: Collapse short multi-line import statements. 2023-08-02 17:41:41 -07:00
test_upload.py retention: Prevent deletion of partially-archived messages. 2023-08-06 13:40:02 -07:00
test_urls.py url tests: Mock out html_to_text. 2023-07-02 16:18:24 -07:00
test_user_groups.py user_groups: Make locks required for updating user group memberships. 2023-08-24 17:21:08 -07:00
test_user_status.py test_classes: Rename and refactor 'tornado_redirected_to_list'. 2023-04-07 09:45:26 -07:00
test_user_topics.py ruff: Collapse short multi-line import statements. 2023-08-02 17:41:41 -07:00
test_users.py message: Access realm object directly from message. 2023-08-23 11:38:32 -07:00
test_validators.py validators: Split out test_validators. 2023-08-11 16:43:12 -07:00
test_webhooks_common.py backend-tests: Update "private message" or "PM" to "direct message". 2023-06-23 11:24:13 -07:00
test_widgets.py tests: Consistently JSON-encode ‘to’ parameter 2022-09-13 11:05:37 -07:00
test_zcommand.py tests: Refactor away result.json() calls with helpers. 2022-06-06 23:06:00 -07:00
test_zephyr.py zephyr: Check PERSONAL_ZMIRROR_SERVER before updating cache. 2022-08-12 17:08:04 -07:00