mirror of
				https://github.com/zulip/zulip.git
				synced 2025-11-03 21:43:21 +00:00 
			
		
		
		
	demo-orgs: Delete expired demo orgs in archive_messages cron job.
Adds delete_expired_demo_organizations to the archive_messages management command, which is run as a cron job. Adds "demo_expired" as a `RealmDeactivationReasonType` to be used for this specific case of calling do_deactivate_realm. The function loops through non-deactivated realms that have a demo organization scheduled deletion datetime set that is less than the current datetime.
This commit is contained in:
		
				
					committed by
					
						
						Tim Abbott
					
				
			
			
				
	
			
			
			
						parent
						
							0fa5b158df
						
					
				
				
					commit
					c797c481b3
				
			@@ -16,6 +16,7 @@ from zerver.actions.custom_profile_fields import do_remove_realm_custom_profile_
 | 
				
			|||||||
from zerver.actions.message_delete import do_delete_messages_by_sender
 | 
					from zerver.actions.message_delete import do_delete_messages_by_sender
 | 
				
			||||||
from zerver.actions.user_groups import update_users_in_full_members_system_group
 | 
					from zerver.actions.user_groups import update_users_in_full_members_system_group
 | 
				
			||||||
from zerver.actions.user_settings import do_delete_avatar_image
 | 
					from zerver.actions.user_settings import do_delete_avatar_image
 | 
				
			||||||
 | 
					from zerver.lib.demo_organizations import demo_organization_owner_email_exists
 | 
				
			||||||
from zerver.lib.exceptions import JsonableError
 | 
					from zerver.lib.exceptions import JsonableError
 | 
				
			||||||
from zerver.lib.message import parse_message_time_limit_setting, update_first_visible_message_id
 | 
					from zerver.lib.message import parse_message_time_limit_setting, update_first_visible_message_id
 | 
				
			||||||
from zerver.lib.queue import queue_json_publish_rollback_unsafe
 | 
					from zerver.lib.queue import queue_json_publish_rollback_unsafe
 | 
				
			||||||
@@ -533,6 +534,7 @@ RealmDeactivationReasonType = Literal[
 | 
				
			|||||||
    "tos_violation",
 | 
					    "tos_violation",
 | 
				
			||||||
    "inactivity",
 | 
					    "inactivity",
 | 
				
			||||||
    "self_hosting_migration",
 | 
					    "self_hosting_migration",
 | 
				
			||||||
 | 
					    "demo_expired",
 | 
				
			||||||
    # When we change the subdomain of a realm, we leave
 | 
					    # When we change the subdomain of a realm, we leave
 | 
				
			||||||
    # behind a deactivated gravestone realm.
 | 
					    # behind a deactivated gravestone realm.
 | 
				
			||||||
    "subdomain_change",
 | 
					    "subdomain_change",
 | 
				
			||||||
@@ -625,6 +627,26 @@ def do_deactivate_realm(
 | 
				
			|||||||
        do_send_realm_deactivation_email(realm, acting_user, deletion_delay_days)
 | 
					        do_send_realm_deactivation_email(realm, acting_user, deletion_delay_days)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def delete_expired_demo_organizations() -> None:
 | 
				
			||||||
 | 
					    demo_organizations_to_delete = Realm.objects.filter(
 | 
				
			||||||
 | 
					        deactivated=False, demo_organization_scheduled_deletion_date__lte=timezone_now()
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    for demo_organization in demo_organizations_to_delete:
 | 
				
			||||||
 | 
					        email_owners = False
 | 
				
			||||||
 | 
					        if demo_organization_owner_email_exists(demo_organization):
 | 
				
			||||||
 | 
					            email_owners = True
 | 
				
			||||||
 | 
					        # By setting deletion_delay_days to zero, we send an event to
 | 
				
			||||||
 | 
					        # the deferred work queue to scrub the realm data when
 | 
				
			||||||
 | 
					        # deactivating the realm.
 | 
				
			||||||
 | 
					        do_deactivate_realm(
 | 
				
			||||||
 | 
					            realm=demo_organization,
 | 
				
			||||||
 | 
					            acting_user=None,
 | 
				
			||||||
 | 
					            deactivation_reason="demo_expired",
 | 
				
			||||||
 | 
					            deletion_delay_days=0,
 | 
				
			||||||
 | 
					            email_owners=email_owners,
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def do_reactivate_realm(realm: Realm) -> None:
 | 
					def do_reactivate_realm(realm: Realm) -> None:
 | 
				
			||||||
    if not realm.deactivated:
 | 
					    if not realm.deactivated:
 | 
				
			||||||
        logging.warning("Realm %s cannot be reactivated because it is already active.", realm.id)
 | 
					        logging.warning("Realm %s cannot be reactivated because it is already active.", realm.id)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -4,10 +4,14 @@ from zerver.lib.exceptions import JsonableError
 | 
				
			|||||||
from zerver.models.realms import Realm
 | 
					from zerver.models.realms import Realm
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def demo_organization_owner_email_exists(realm: Realm) -> bool:
 | 
				
			||||||
 | 
					    human_owner_emails = set(realm.get_human_owner_users().values_list("delivery_email", flat=True))
 | 
				
			||||||
 | 
					    return human_owner_emails != {""}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def check_demo_organization_has_set_email(realm: Realm) -> None:
 | 
					def check_demo_organization_has_set_email(realm: Realm) -> None:
 | 
				
			||||||
    # This should be called after checking that the realm has
 | 
					    # This should be called after checking that the realm has
 | 
				
			||||||
    # a demo_organization_scheduled_deletion_date set.
 | 
					    # a demo_organization_scheduled_deletion_date set.
 | 
				
			||||||
    assert realm.demo_organization_scheduled_deletion_date is not None
 | 
					    assert realm.demo_organization_scheduled_deletion_date is not None
 | 
				
			||||||
    human_owner_emails = set(realm.get_human_owner_users().values_list("delivery_email", flat=True))
 | 
					    if not demo_organization_owner_email_exists(realm):
 | 
				
			||||||
    if "" in human_owner_emails:
 | 
					 | 
				
			||||||
        raise JsonableError(_("Configure owner account email address."))
 | 
					        raise JsonableError(_("Configure owner account email address."))
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,7 +2,10 @@ from typing import Any
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
from typing_extensions import override
 | 
					from typing_extensions import override
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from zerver.actions.realm_settings import clean_deactivated_realm_data
 | 
					from zerver.actions.realm_settings import (
 | 
				
			||||||
 | 
					    clean_deactivated_realm_data,
 | 
				
			||||||
 | 
					    delete_expired_demo_organizations,
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
from zerver.lib.management import ZulipBaseCommand, abort_unless_locked
 | 
					from zerver.lib.management import ZulipBaseCommand, abort_unless_locked
 | 
				
			||||||
from zerver.lib.retention import archive_messages, clean_archived_data
 | 
					from zerver.lib.retention import archive_messages, clean_archived_data
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -13,4 +16,12 @@ class Command(ZulipBaseCommand):
 | 
				
			|||||||
    def handle(self, *args: Any, **options: str) -> None:
 | 
					    def handle(self, *args: Any, **options: str) -> None:
 | 
				
			||||||
        clean_archived_data()
 | 
					        clean_archived_data()
 | 
				
			||||||
        archive_messages()
 | 
					        archive_messages()
 | 
				
			||||||
        clean_deactivated_realm_data()
 | 
					        scrub_realms()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def scrub_realms() -> None:
 | 
				
			||||||
 | 
					    # First, scrub currently deactivated realms that have an expired
 | 
				
			||||||
 | 
					    # scheduled deletion date. Then, deactivate and scrub realms with
 | 
				
			||||||
 | 
					    # an expired scheduled demo organization deletion date.
 | 
				
			||||||
 | 
					    clean_deactivated_realm_data()
 | 
				
			||||||
 | 
					    delete_expired_demo_organizations()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -26,6 +26,7 @@ from zerver.actions.message_send import (
 | 
				
			|||||||
)
 | 
					)
 | 
				
			||||||
from zerver.actions.realm_settings import (
 | 
					from zerver.actions.realm_settings import (
 | 
				
			||||||
    clean_deactivated_realm_data,
 | 
					    clean_deactivated_realm_data,
 | 
				
			||||||
 | 
					    delete_expired_demo_organizations,
 | 
				
			||||||
    do_add_deactivated_redirect,
 | 
					    do_add_deactivated_redirect,
 | 
				
			||||||
    do_change_realm_max_invites,
 | 
					    do_change_realm_max_invites,
 | 
				
			||||||
    do_change_realm_org_type,
 | 
					    do_change_realm_org_type,
 | 
				
			||||||
@@ -1225,6 +1226,64 @@ class RealmTest(ZulipTestCase):
 | 
				
			|||||||
            clean_deactivated_realm_data()
 | 
					            clean_deactivated_realm_data()
 | 
				
			||||||
            mock_scrub_realm.assert_called_once_with(zephyr, acting_user=None)
 | 
					            mock_scrub_realm.assert_called_once_with(zephyr, acting_user=None)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test_delete_expired_demo_organizations(self) -> None:
 | 
				
			||||||
 | 
					        zulip = get_realm("zulip")
 | 
				
			||||||
 | 
					        assert not zulip.deactivated
 | 
				
			||||||
 | 
					        assert zulip.demo_organization_scheduled_deletion_date is None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        with mock.patch(
 | 
				
			||||||
 | 
					            "zerver.actions.realm_settings.do_deactivate_realm"
 | 
				
			||||||
 | 
					        ) as mock_deactivate_realm:
 | 
				
			||||||
 | 
					            delete_expired_demo_organizations()
 | 
				
			||||||
 | 
					            mock_deactivate_realm.assert_not_called()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Add scheduled demo organization deletion date
 | 
				
			||||||
 | 
					        zulip.demo_organization_scheduled_deletion_date = timezone_now() + timedelta(days=4)
 | 
				
			||||||
 | 
					        zulip.save()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # Before deletion date
 | 
				
			||||||
 | 
					        with mock.patch(
 | 
				
			||||||
 | 
					            "zerver.actions.realm_settings.do_deactivate_realm"
 | 
				
			||||||
 | 
					        ) as mock_deactivate_realm:
 | 
				
			||||||
 | 
					            delete_expired_demo_organizations()
 | 
				
			||||||
 | 
					            mock_deactivate_realm.assert_not_called()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # After deletion date, when owner email is set.
 | 
				
			||||||
 | 
					        with (
 | 
				
			||||||
 | 
					            time_machine.travel(timezone_now() + timedelta(days=5), tick=False),
 | 
				
			||||||
 | 
					            mock.patch(
 | 
				
			||||||
 | 
					                "zerver.actions.realm_settings.do_deactivate_realm"
 | 
				
			||||||
 | 
					            ) as mock_deactivate_realm,
 | 
				
			||||||
 | 
					        ):
 | 
				
			||||||
 | 
					            delete_expired_demo_organizations()
 | 
				
			||||||
 | 
					            mock_deactivate_realm.assert_called_once_with(
 | 
				
			||||||
 | 
					                realm=zulip,
 | 
				
			||||||
 | 
					                acting_user=None,
 | 
				
			||||||
 | 
					                deactivation_reason="demo_expired",
 | 
				
			||||||
 | 
					                deletion_delay_days=0,
 | 
				
			||||||
 | 
					                email_owners=True,
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # After deletion date, when owner email is not set.
 | 
				
			||||||
 | 
					        desdemona = self.example_user("desdemona")
 | 
				
			||||||
 | 
					        desdemona.delivery_email = ""
 | 
				
			||||||
 | 
					        desdemona.save()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        with (
 | 
				
			||||||
 | 
					            time_machine.travel(timezone_now() + timedelta(days=5), tick=False),
 | 
				
			||||||
 | 
					            mock.patch(
 | 
				
			||||||
 | 
					                "zerver.actions.realm_settings.do_deactivate_realm"
 | 
				
			||||||
 | 
					            ) as mock_deactivate_realm,
 | 
				
			||||||
 | 
					        ):
 | 
				
			||||||
 | 
					            delete_expired_demo_organizations()
 | 
				
			||||||
 | 
					            mock_deactivate_realm.assert_called_once_with(
 | 
				
			||||||
 | 
					                realm=zulip,
 | 
				
			||||||
 | 
					                acting_user=None,
 | 
				
			||||||
 | 
					                deactivation_reason="demo_expired",
 | 
				
			||||||
 | 
					                deletion_delay_days=0,
 | 
				
			||||||
 | 
					                email_owners=False,
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def test_initial_plan_type(self) -> None:
 | 
					    def test_initial_plan_type(self) -> None:
 | 
				
			||||||
        with self.settings(BILLING_ENABLED=True):
 | 
					        with self.settings(BILLING_ENABLED=True):
 | 
				
			||||||
            self.assertEqual(do_create_realm("hosted", "hosted").plan_type, Realm.PLAN_TYPE_LIMITED)
 | 
					            self.assertEqual(do_create_realm("hosted", "hosted").plan_type, Realm.PLAN_TYPE_LIMITED)
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user