diff --git a/vc_zoom/indico_vc_zoom/plugin.py b/vc_zoom/indico_vc_zoom/plugin.py index c986b14c..7391de87 100644 --- a/vc_zoom/indico_vc_zoom/plugin.py +++ b/vc_zoom/indico_vc_zoom/plugin.py @@ -446,11 +446,13 @@ def update_data_vc_room(self, vc_room, data, is_new=False): flag_modified(vc_room, 'data') - # Push approval_type whenever auto_register is toggled. On enable we also sync any - # existing registrants so the initial backfill can hit the registrants endpoint. + # Push approval_type whenever auto_register is toggled. Manual approval (1) keeps the public + # registration page gated (self-registrants stay "pending"); Indico-pushed registrants are + # auto-approved via auto_approve. On enable we also sync any existing registrants so the + # initial backfill can hit the registrants endpoint. if not is_new and auto_register_before != vc_room.data.get('auto_register'): is_webinar = vc_room.data.get('meeting_type') == 'webinar' - desired_approval_type = 0 if vc_room.data.get('auto_register') else 2 + desired_approval_type = 1 if vc_room.data.get('auto_register') else 2 self._push_approval_type(vc_room, desired_approval_type, is_webinar=is_webinar) if vc_room.data.get('auto_register'): candidates = [ @@ -532,7 +534,12 @@ def create_room(self, vc_room, event): 'join_before_host': self.settings.get('join_before_host'), }) if vc_room.data.get('auto_register'): - settings['approval_type'] = 0 + # Manual approval (1), not automatic (0): the public Zoom registration page holds + # self-registrants in "pending", so a leaked link can't self-approve into the + # meeting. Indico's own registrants are still approved via the auto_approve flag on + # the registrant push, which Zoom honors whenever the meeting's current + # approval_type is 1. + settings['approval_type'] = 1 kwargs.update({ 'topic': vc_room.name, @@ -624,7 +631,8 @@ def update_room(self, vc_room, event): if vc_room.data['waiting_room'] != zoom_meeting_settings['waiting_room']: changes.setdefault('settings', {})['waiting_room'] = vc_room.data['waiting_room'] - desired_approval_type = 0 if vc_room.data.get('auto_register') else 2 + # Manual approval (1) when auto_register is on; see create_room for the rationale. + desired_approval_type = 1 if vc_room.data.get('auto_register') else 2 approval_type_changed = zoom_meeting_settings.get('approval_type') != desired_approval_type if approval_type_changed: changes.setdefault('settings', {})['approval_type'] = desired_approval_type diff --git a/vc_zoom/tests/operation_test.py b/vc_zoom/tests/operation_test.py index 3ddab97c..58f7cf44 100644 --- a/vc_zoom/tests/operation_test.py +++ b/vc_zoom/tests/operation_test.py @@ -5,7 +5,7 @@ # them and/or modify them under the terms of the MIT License; # see the LICENSE file for more details. -from datetime import datetime +from datetime import datetime, timedelta from zoneinfo import ZoneInfo import pytest @@ -120,9 +120,9 @@ def _get_meeting(self, meeting_id): @pytest.mark.usefixtures('auto_register_before') @pytest.mark.parametrize('meeting_type', ('regular', 'webinar')) @pytest.mark.parametrize(('auto_register_before', 'auto_register_after', 'current_approval_type', 'expected'), ( - (False, True, 2, {'settings': {'approval_type': 0}}), - (True, False, 0, {'settings': {'approval_type': 2}}), - (True, True, 0, None), + (False, True, 2, {'settings': {'approval_type': 1}}), + (True, False, 1, {'settings': {'approval_type': 2}}), + (True, True, 1, None), (False, False, 2, None), )) def test_update_room_pushes_approval_type( @@ -163,7 +163,7 @@ def test_update_room_pushes_approval_type( @pytest.mark.parametrize('meeting_type', ('regular', 'webinar')) @pytest.mark.parametrize(('auto_register_before', 'auto_register_after', 'expected_approval_type'), ( - (False, True, 0), + (False, True, 1), (True, False, 2), )) def test_update_data_vc_room_pushes_approval_type_before_sync( @@ -219,6 +219,31 @@ def test_create_room_blocks_past_event_with_auto_register( zoom_api['create_meeting'].assert_not_called() +def test_create_room_with_auto_register_uses_manual_approval( + create_event, create_zoom_meeting, zoom_plugin, zoom_api, +): + """auto_register meetings are created with approval_type=1 so the public registration page + holds self-registrants in 'pending'; Indico's own registrants are approved via auto_approve. + """ + event = create_event( + creator=zoom_api['user'], + start_dt=datetime.now(TZ) + timedelta(days=30), + end_dt=datetime.now(TZ) + timedelta(days=30, hours=2), + title='Future Event', + creator_has_privileges=True, + ) + + vc_room = create_zoom_meeting(event, 'event') + vc_room.data['auto_register'] = True + zoom_api['create_meeting'].reset_mock() + + zoom_plugin.create_room(vc_room, event) + + zoom_api['create_meeting'].assert_called_once() + settings = zoom_api['create_meeting'].call_args.kwargs['settings'] + assert settings['approval_type'] == 1 + + @pytest.mark.parametrize('is_webinar', (False, True)) def test_push_approval_type_warns_when_zoom_drops_patch( mocker, create_event, create_zoom_meeting, zoom_plugin, zoom_api, is_webinar,