realm_export: Add realm_export_consent feature to API.

Fixes part of #31201.
This commit is contained in:
Prakhar Pratyush 2024-10-10 18:08:44 +05:30 committed by Tim Abbott
parent 70b6e46516
commit eaee5763d6
8 changed files with 124 additions and 1 deletions

View File

@ -20,6 +20,12 @@ format used by the Zulip server that they are interacting with.
## Changes in Zulip 10.0
**Feature level 312**
* [`GET /events`](/api/get-events): Added `realm_export_consent` event
type to allow realm administrators to view which users have
consented to export their private data as part of a realm export.
**Feature level 311**
* [`POST /user_groups/{user_group_id}/members`](/api/update-user-group-members):

View File

@ -34,7 +34,7 @@ DESKTOP_WARNING_VERSION = "5.9.3"
# new level means in api_docs/changelog.md, as well as "**Changes**"
# entries in the endpoint's documentation in `zulip.yaml`.
API_FEATURE_LEVEL = 311 # Last bumped for updating subgroups.
API_FEATURE_LEVEL = 312 # Last bumped for adding 'realm_export_consent' event type.
# Bump the minor PROVISION_VERSION to indicate that folks should provision
# only when going from an old version of the code to a newer version. Bump

View File

@ -731,6 +731,18 @@ def do_reactivate_user(user_profile: UserProfile, *, acting_user: UserProfile |
)
send_event_on_commit(user_profile.realm, event, get_user_ids_who_can_access_user(user_profile))
if not user_profile.is_bot:
realm_export_consent_event = dict(
type="realm_export_consent",
user_id=user_profile.id,
consented=user_profile.allow_private_data_export,
)
send_event_on_commit(
user_profile.realm,
realm_export_consent_event,
list(user_profile.realm.get_human_admin_users().values_list("id", flat=True)),
)
if user_profile.is_bot:
event = dict(
type="realm_bot",

View File

@ -512,6 +512,18 @@ def do_change_user_setting(
send_event_on_commit(user_profile.realm, legacy_event, [user_profile.id])
if setting_name == "allow_private_data_export":
event = {
"type": "realm_export_consent",
"user_id": user_profile.id,
"consented": setting_value,
}
send_event_on_commit(
user_profile.realm,
event,
list(user_profile.realm.get_human_admin_users().values_list("id", flat=True)),
)
# Updates to the time zone display setting are sent to all users
if setting_name == "timezone":
payload = dict(

View File

@ -872,6 +872,15 @@ def check_realm_export(
assert has_failed_timestamp == (export["failed_timestamp"] is not None)
realm_export_consent_event = event_dict_type(
[
("type", Equals("realm_export_consent")),
("user_id", int),
("consented", bool),
]
)
check_realm_export_consent = make_checker(realm_export_consent_event)
realm_linkifier_type = DictType(
required_keys=[
("pattern", str),

View File

@ -1564,6 +1564,10 @@ def apply_event(
# These realm export events are only available to
# administrators, and aren't included in page_params.
pass
elif event["type"] == "realm_export_consent":
# These 'realm_export_consent' events are only available to
# administrators, and aren't included in page_params.
pass
elif event["type"] == "alert_words":
state["alert_words"] = event["alert_words"]
elif event["type"] == "muted_topics":

View File

@ -3884,6 +3884,41 @@ paths:
],
"id": 1,
}
- type: object
additionalProperties: false
description: |
Event sent to administrators when the [data export
consent][help-export-consent] status for a user changes, whether due
to a user changing their consent preferences or a user being created
or reactivated (since user creation/activation events do not contain
these data).
[help-export-consent]: /help/export-your-organization#configure-whether-administrators-can-export-your-private-data
**Changes**: New in Zulip 10.0 (feature level 312). Previously,
there was not event available to administrators with these data.
properties:
id:
$ref: "#/components/schemas/EventIdSchema"
type:
allOf:
- $ref: "#/components/schemas/EventTypeSchema"
- enum:
- realm_export_consent
user_id:
type: integer
description: |
The ID of the user whose setting was changed.
consented:
type: boolean
description: |
Whether the user has consented for their private data export.
example:
{
"type": "realm_export_consent",
"user_id": 1,
"consented": true,
}
- type: object
additionalProperties: false
description: |

View File

@ -166,6 +166,7 @@ from zerver.lib.event_schema import (
check_realm_domains_remove,
check_realm_emoji_update,
check_realm_export,
check_realm_export_consent,
check_realm_linkifiers,
check_realm_playgrounds,
check_realm_update,
@ -3285,6 +3286,17 @@ class NormalActionsTest(BaseAction):
check_realm_user_update("events[0]", events[0], "is_active")
check_user_group_add_members("events[1]", events[1])
# Verify that admins receive 'realm_export_consent' event
# when a user is reactivated.
do_deactivate_user(user_profile, acting_user=None)
self.user_profile = self.example_user("iago")
with self.verify_action(num_events=4) as events:
do_reactivate_user(user_profile, acting_user=None)
check_realm_user_update("events[0]", events[0], "is_active")
check_realm_export_consent("events[1]", events[1])
check_subscription_peer_add("events[2]", events[2])
check_user_group_add_members("events[3]", events[3])
def test_do_deactivate_realm(self) -> None:
realm = self.user_profile.realm
@ -4248,6 +4260,39 @@ class UserDisplayActionTest(BaseAction):
if prop not in UserProfile.notification_setting_types:
self.do_change_user_settings_test(prop)
def test_set_allow_private_data_export(self) -> None:
# Verify that both 'user_settings' and 'realm_export_consent' events
# are received by admins when they change the setting.
do_change_user_role(
self.user_profile, UserProfile.ROLE_REALM_ADMINISTRATOR, acting_user=None
)
self.assertFalse(self.user_profile.allow_private_data_export)
num_events = 2
with self.verify_action(num_events=num_events) as events:
do_change_user_setting(
self.user_profile,
"allow_private_data_export",
True,
acting_user=self.user_profile,
)
check_user_settings_update("events[0]", events[0])
check_realm_export_consent("events[1]", events[1])
# Verify that only 'realm_export_consent' event is received
# by admins when an another user changes their setting.
cordelia = self.example_user("cordelia")
self.assertFalse(cordelia.allow_private_data_export)
num_events = 1
with self.verify_action(num_events=num_events, state_change_expected=False) as events:
do_change_user_setting(
cordelia,
"allow_private_data_export",
True,
acting_user=cordelia,
)
check_realm_export_consent("events[0]", events[0])
def test_set_user_timezone(self) -> None:
values = ["America/Denver", "Pacific/Pago_Pago", "Pacific/Galapagos", ""]
num_events = 3