mirror of
https://github.com/zulip/zulip.git
synced 2025-10-23 04:52:12 +00:00
export: Also add guardrail to the management command.
This commit is contained in:
committed by
Tim Abbott
parent
8ab400b95d
commit
716ead8f4e
@@ -3034,3 +3034,30 @@ def do_common_export_processes(output_dir: str) -> None:
|
|||||||
|
|
||||||
logging.info("Exporting migration status")
|
logging.info("Exporting migration status")
|
||||||
export_migration_status(output_dir)
|
export_migration_status(output_dir)
|
||||||
|
|
||||||
|
|
||||||
|
def check_export_with_consent_is_usable(realm: Realm) -> bool:
|
||||||
|
# Users without consent enabled will end up deactivated in the exported
|
||||||
|
# data. An organization without a consenting Owner would therefore not be
|
||||||
|
# functional after export->import. That's most likely not desired by the user
|
||||||
|
# so check for such a case.
|
||||||
|
consented_user_ids = get_consented_user_ids(realm)
|
||||||
|
return UserProfile.objects.filter(
|
||||||
|
id__in=consented_user_ids, role=UserProfile.ROLE_REALM_OWNER, realm=realm
|
||||||
|
).exists()
|
||||||
|
|
||||||
|
|
||||||
|
def check_public_export_is_usable(realm: Realm) -> bool:
|
||||||
|
# Since users with email visibility set to NOBODY won't have their real emails
|
||||||
|
# exported, this could result in a lack of functional Owner accounts.
|
||||||
|
# We make sure that at least one Owner can have their real email address exported.
|
||||||
|
return UserProfile.objects.filter(
|
||||||
|
role=UserProfile.ROLE_REALM_OWNER,
|
||||||
|
email_address_visibility__in=[
|
||||||
|
UserProfile.EMAIL_ADDRESS_VISIBILITY_EVERYONE,
|
||||||
|
UserProfile.EMAIL_ADDRESS_VISIBILITY_MEMBERS,
|
||||||
|
UserProfile.EMAIL_ADDRESS_VISIBILITY_MODERATORS,
|
||||||
|
UserProfile.EMAIL_ADDRESS_VISIBILITY_ADMINS,
|
||||||
|
],
|
||||||
|
realm=realm,
|
||||||
|
).exists()
|
||||||
|
@@ -9,7 +9,11 @@ from django.utils.timezone import now as timezone_now
|
|||||||
from typing_extensions import override
|
from typing_extensions import override
|
||||||
|
|
||||||
from zerver.actions.realm_settings import do_deactivate_realm
|
from zerver.actions.realm_settings import do_deactivate_realm
|
||||||
from zerver.lib.export import export_realm_wrapper
|
from zerver.lib.export import (
|
||||||
|
check_export_with_consent_is_usable,
|
||||||
|
check_public_export_is_usable,
|
||||||
|
export_realm_wrapper,
|
||||||
|
)
|
||||||
from zerver.lib.management import ZulipBaseCommand
|
from zerver.lib.management import ZulipBaseCommand
|
||||||
from zerver.models import RealmExport
|
from zerver.models import RealmExport
|
||||||
|
|
||||||
@@ -102,6 +106,11 @@ class Command(ZulipBaseCommand):
|
|||||||
action="store_true",
|
action="store_true",
|
||||||
help="Whether to export private data of users who consented",
|
help="Whether to export private data of users who consented",
|
||||||
)
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--force",
|
||||||
|
action="store_true",
|
||||||
|
help="Skip checks for whether the generated export will be a usable realm.",
|
||||||
|
)
|
||||||
parser.add_argument(
|
parser.add_argument(
|
||||||
"--upload",
|
"--upload",
|
||||||
action="store_true",
|
action="store_true",
|
||||||
@@ -117,6 +126,7 @@ class Command(ZulipBaseCommand):
|
|||||||
output_dir = options["output_dir"]
|
output_dir = options["output_dir"]
|
||||||
public_only = options["public_only"]
|
public_only = options["public_only"]
|
||||||
export_full_with_consent = options["export_full_with_consent"]
|
export_full_with_consent = options["export_full_with_consent"]
|
||||||
|
assert not (public_only and export_full_with_consent)
|
||||||
|
|
||||||
print(f"\033[94mExporting realm\033[0m: {realm.string_id}")
|
print(f"\033[94mExporting realm\033[0m: {realm.string_id}")
|
||||||
|
|
||||||
@@ -151,6 +161,15 @@ class Command(ZulipBaseCommand):
|
|||||||
f"Refusing to overwrite existing tarball: {tarball_path}. Aborting..."
|
f"Refusing to overwrite existing tarball: {tarball_path}. Aborting..."
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (not options["force"]) and (
|
||||||
|
(export_full_with_consent and not check_export_with_consent_is_usable(realm))
|
||||||
|
or (public_only and not check_public_export_is_usable(realm))
|
||||||
|
):
|
||||||
|
raise CommandError(
|
||||||
|
"The generated export will not be a usable organization! "
|
||||||
|
"You can pass --force to skip this check."
|
||||||
|
)
|
||||||
|
|
||||||
if options["deactivate_realm"]:
|
if options["deactivate_realm"]:
|
||||||
print(f"\033[94mDeactivating realm\033[0m: {realm.string_id}")
|
print(f"\033[94mDeactivating realm\033[0m: {realm.string_id}")
|
||||||
do_deactivate_realm(
|
do_deactivate_realm(
|
||||||
|
@@ -519,7 +519,7 @@ class TestExport(ZulipTestCase):
|
|||||||
self.example_user("iago"), "allow_private_data_export", True, acting_user=None
|
self.example_user("iago"), "allow_private_data_export", True, acting_user=None
|
||||||
)
|
)
|
||||||
do_change_user_setting(
|
do_change_user_setting(
|
||||||
self.example_user("iago"), "allow_private_data_export", True, acting_user=None
|
self.example_user("desdemona"), "allow_private_data_export", True, acting_user=None
|
||||||
)
|
)
|
||||||
|
|
||||||
with (
|
with (
|
||||||
|
@@ -11,7 +11,11 @@ from analytics.models import RealmCount
|
|||||||
from zerver.actions.realm_export import do_delete_realm_export, notify_realm_export
|
from zerver.actions.realm_export import do_delete_realm_export, notify_realm_export
|
||||||
from zerver.decorator import require_realm_admin
|
from zerver.decorator import require_realm_admin
|
||||||
from zerver.lib.exceptions import JsonableError
|
from zerver.lib.exceptions import JsonableError
|
||||||
from zerver.lib.export import get_consented_user_ids, get_realm_exports_serialized
|
from zerver.lib.export import (
|
||||||
|
check_export_with_consent_is_usable,
|
||||||
|
check_public_export_is_usable,
|
||||||
|
get_realm_exports_serialized,
|
||||||
|
)
|
||||||
from zerver.lib.queue import queue_event_on_commit
|
from zerver.lib.queue import queue_event_on_commit
|
||||||
from zerver.lib.response import json_success
|
from zerver.lib.response import json_success
|
||||||
from zerver.lib.send_email import FromAddress
|
from zerver.lib.send_email import FromAddress
|
||||||
@@ -82,43 +86,24 @@ def export_realm(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
if export_type == RealmExport.EXPORT_FULL_WITH_CONSENT:
|
if (
|
||||||
# Users without consent enabled will end up deactivated in the exported
|
export_type == RealmExport.EXPORT_FULL_WITH_CONSENT
|
||||||
# data. An organization without a consenting Owner would therefore not be
|
and not check_export_with_consent_is_usable(realm)
|
||||||
# functional after export->import. That's most likely not desired by the user
|
):
|
||||||
# so check for such a case and return an error.
|
raise JsonableError(
|
||||||
consented_user_ids = get_consented_user_ids(realm)
|
_(
|
||||||
if not UserProfile.objects.filter(
|
"Make sure at least one Organization Owner is consenting to the export "
|
||||||
id__in=consented_user_ids, role=UserProfile.ROLE_REALM_OWNER, realm=realm
|
"or contact {email} for help."
|
||||||
).exists():
|
).format(email=FromAddress.SUPPORT)
|
||||||
raise JsonableError(
|
)
|
||||||
_(
|
elif export_type == RealmExport.EXPORT_PUBLIC and not check_public_export_is_usable(realm):
|
||||||
"Make sure at least one Organization Owner is consenting to the export "
|
raise JsonableError(
|
||||||
"or contact {email} for help."
|
_(
|
||||||
).format(email=FromAddress.SUPPORT)
|
"Make sure at least one Organization Owner allows other "
|
||||||
)
|
"Administrators to see their email address "
|
||||||
elif export_type == RealmExport.EXPORT_PUBLIC:
|
"or contact {email} for help"
|
||||||
# Since users with email visibility set to NOBODY won't have their real emails
|
).format(email=FromAddress.SUPPORT)
|
||||||
# exported, this could result in a lack of functional Owner accounts.
|
)
|
||||||
# We make sure that at least one Owner can have their real email address exported
|
|
||||||
# or return an error.
|
|
||||||
if not UserProfile.objects.filter(
|
|
||||||
role=UserProfile.ROLE_REALM_OWNER,
|
|
||||||
email_address_visibility__in=[
|
|
||||||
UserProfile.EMAIL_ADDRESS_VISIBILITY_EVERYONE,
|
|
||||||
UserProfile.EMAIL_ADDRESS_VISIBILITY_MEMBERS,
|
|
||||||
UserProfile.EMAIL_ADDRESS_VISIBILITY_MODERATORS,
|
|
||||||
UserProfile.EMAIL_ADDRESS_VISIBILITY_ADMINS,
|
|
||||||
],
|
|
||||||
realm=realm,
|
|
||||||
).exists():
|
|
||||||
raise JsonableError(
|
|
||||||
_(
|
|
||||||
"Make sure at least one Organization Owner allows other "
|
|
||||||
"Administrators to see their email address "
|
|
||||||
"or contact {email} for help"
|
|
||||||
).format(email=FromAddress.SUPPORT)
|
|
||||||
)
|
|
||||||
|
|
||||||
row = RealmExport.objects.create(
|
row = RealmExport.objects.create(
|
||||||
realm=realm,
|
realm=realm,
|
||||||
|
Reference in New Issue
Block a user