From a55f5275217373985232e85974143a34ff5d3383 Mon Sep 17 00:00:00 2001 From: luisa-beerboom <101706784+luisa-beerboom@users.noreply.github.com> Date: Thu, 18 Jul 2024 15:40:18 +0200 Subject: [PATCH] Add setting for closed meeting (#2523) * Add setting for closed meeting * Edit meeting.update and permission system, test almost everything * Forbid locking template meetings * Add all but the user.create tests * Add tests to user.create * Edit send_email permission handling * Fix send_invitation_email permission tests * Amend user.set_present * Amend user.toggle_presence_by_number * Amend user.create and user.update * Amend create update perm mixin for users * Forbid cloning of locked meetings * Ensure archiving still works * Amend search_for_id_by_external_id presenter * Fix test * Update datastore calls and wiki entries --- docs/actions/meeting.clone.md | 2 + docs/actions/meeting.import.md | 1 + docs/actions/meeting.update.md | 4 +- docs/actions/participant.json_upload.md | 2 +- docs/actions/user.assign_meetings.md | 2 + docs/actions/user.create.md | 8 +- docs/actions/user.send_invitation_email.md | 3 +- docs/actions/user.set_present.md | 5 +- .../actions/user.toggle_presence_by_number.md | 5 +- docs/actions/user.update.md | 9 +- docs/presenters/export_meeting.md | 1 + docs/presenters/get_user_related_models.md | 4 +- .../search_for_id_by_external_id.md | 2 + global/meta | 2 +- .../action/actions/meeting/clone.py | 3 + .../action/actions/meeting/import_.py | 5 + .../action/actions/meeting/update.py | 19 ++ .../action/actions/user/assign_meetings.py | 19 ++ .../user/create_update_permissions_mixin.py | 117 ++++++--- .../action/actions/user/participant_common.py | 32 ++- .../actions/user/send_invitation_email.py | 2 +- .../action/actions/user/set_present.py | 27 +- .../actions/user/toggle_presence_by_number.py | 27 +- openslides_backend/models/models.py | 1 + .../permissions/permission_helper.py | 48 ++-- .../presenter/export_meeting.py | 11 +- .../presenter/get_user_related_models.py | 14 +- .../presenter/search_for_id_by_external_id.py | 50 +++- .../system/action/agenda_item/test_assign.py | 16 +- .../system/action/agenda_item/test_create.py | 7 + .../system/action/agenda_item/test_delete.py | 10 + .../action/agenda_item/test_numbering.py | 22 ++ tests/system/action/agenda_item/test_sort.py | 9 + .../system/action/agenda_item/test_update.py | 7 + tests/system/action/assignment/test_create.py | 10 + tests/system/action/assignment/test_delete.py | 9 + tests/system/action/assignment/test_update.py | 9 + .../assignment_candidate/test_create.py | 2 +- .../action/assignment_candidate/test_sort.py | 7 + tests/system/action/base.py | 18 ++ .../base_motion_meeting_user_create_test.py | 7 + .../base_motion_meeting_user_delete_test.py | 7 + .../base_motion_meeting_user_sort_test.py | 7 + tests/system/action/chat_group/test_clear.py | 7 + tests/system/action/chat_group/test_create.py | 10 + tests/system/action/chat_group/test_delete.py | 7 + tests/system/action/chat_group/test_sort.py | 7 + tests/system/action/chat_group/test_update.py | 7 + .../system/action/chat_message/test_delete.py | 7 + tests/system/action/group/test_create.py | 7 + tests/system/action/group/test_delete.py | 9 + tests/system/action/group/test_update.py | 7 + .../test_delete_all_speakers.py | 7 + .../list_of_speakers/test_re_add_last.py | 7 + .../action/list_of_speakers/test_update.py | 7 + .../action/mediafile/test_create_directory.py | 11 + tests/system/action/mediafile/test_delete.py | 7 + tests/system/action/mediafile/test_move.py | 7 + tests/system/action/mediafile/test_update.py | 7 + tests/system/action/mediafile/test_upload.py | 12 + tests/system/action/meeting/test_archive.py | 5 + tests/system/action/meeting/test_clone.py | 7 + tests/system/action/meeting/test_delete.py | 11 + .../test_delete_all_speakers_of_all_lists.py | 7 + tests/system/action/meeting/test_import.py | 7 + tests/system/action/meeting/test_set_font.py | 7 + tests/system/action/meeting/test_set_logo.py | 7 + tests/system/action/meeting/test_unarchive.py | 5 + .../system/action/meeting/test_unset_font.py | 7 + .../system/action/meeting/test_unset_logo.py | 7 + tests/system/action/meeting/test_update.py | 178 ++++++++++++- tests/system/action/motion/test_create.py | 11 + tests/system/action/motion/test_delete.py | 7 + .../motion/test_follow_recommendation.py | 7 + .../motion/test_reset_recommendation.py | 7 + .../system/action/motion/test_reset_state.py | 7 + .../action/motion/test_set_recommendation.py | 7 + tests/system/action/motion/test_set_state.py | 7 + .../action/motion/test_set_support_self.py | 7 + tests/system/action/motion/test_sort.py | 7 + tests/system/action/motion/test_update.py | 4 +- .../system/action/motion_block/test_create.py | 13 + .../system/action/motion_block/test_delete.py | 7 + .../system/action/motion_block/test_update.py | 9 + .../action/motion_category/test_create.py | 10 + .../action/motion_category/test_delete.py | 7 + .../motion_category/test_number_motions.py | 7 + .../action/motion_category/test_sort.py | 7 + .../test_sort_motions_in_categories.py | 7 + .../action/motion_category/test_update.py | 10 + .../test_create.py | 12 + .../test_delete.py | 6 +- .../test_update.py | 10 + .../action/motion_comment/test_create.py | 7 + .../action/motion_comment/test_delete.py | 7 + .../motion_comment_section/test_create.py | 7 + .../motion_comment_section/test_delete.py | 7 + .../motion_comment_section/test_sort.py | 7 + .../motion_comment_section/test_update.py | 12 + .../system/action/motion_state/test_create.py | 7 + .../system/action/motion_state/test_delete.py | 7 + tests/system/action/motion_state/test_sort.py | 7 + .../system/action/motion_state/test_update.py | 7 + .../action/motion_workflow/test_create.py | 7 + .../action/motion_workflow/test_delete.py | 17 ++ .../action/motion_workflow/test_update.py | 9 + tests/system/action/option/test_update.py | 7 + .../point_of_order_category/test_create.py | 7 + .../point_of_order_category/test_delete.py | 7 + .../point_of_order_category/test_update.py | 7 + tests/system/action/poll/test_anonymize.py | 7 + tests/system/action/poll/test_create.py | 18 ++ tests/system/action/poll/test_delete.py | 9 +- tests/system/action/poll/test_publish.py | 7 + tests/system/action/poll/test_reset.py | 9 +- tests/system/action/poll/test_stop.py | 13 +- tests/system/action/poll/test_update.py | 7 + tests/system/action/projection/test_delete.py | 7 + .../action/projection/test_update_options.py | 10 + .../action/projector/test_add_to_preview.py | 12 + .../action/projector/test_control_view.py | 7 + tests/system/action/projector/test_create.py | 10 + tests/system/action/projector/test_delete.py | 7 + tests/system/action/projector/test_next.py | 7 + .../system/action/projector/test_previous.py | 7 + tests/system/action/projector/test_project.py | 14 + .../action/projector/test_project_preview.py | 7 + .../action/projector/test_sort_preview.py | 7 + tests/system/action/projector/test_toggle.py | 7 + tests/system/action/projector/test_update.py | 11 + .../action/projector_countdown/test_create.py | 12 + .../action/projector_countdown/test_delete.py | 7 + .../action/projector_countdown/test_update.py | 7 + .../action/projector_message/test_create.py | 10 + .../action/projector_message/test_delete.py | 7 + .../action/projector_message/test_update.py | 7 + tests/system/action/speaker/test_create.py | 7 + tests/system/action/speaker/test_delete.py | 7 + .../system/action/speaker/test_end_speech.py | 7 + tests/system/action/speaker/test_pause.py | 7 + tests/system/action/speaker/test_sort.py | 7 + tests/system/action/speaker/test_speak.py | 7 + tests/system/action/speaker/test_unpause.py | 7 + tests/system/action/speaker/test_update.py | 7 + .../action/structure_level/test_create.py | 10 + .../action/structure_level/test_delete.py | 11 + .../action/structure_level/test_update.py | 12 + .../test_add_time.py | 7 + .../test_create.py | 10 + tests/system/action/topic/test_create.py | 7 + tests/system/action/topic/test_delete.py | 7 + tests/system/action/topic/test_json_upload.py | 7 + tests/system/action/topic/test_update.py | 7 + .../action/user/test_assign_meetings.py | 94 +++++++ tests/system/action/user/test_create.py | 69 +++++ tests/system/action/user/test_delete.py | 3 +- .../system/action/user/test_merge_together.py | 2 +- .../action/user/test_participant_import.py | 5 + .../user/test_participant_json_upload.py | 7 + .../action/user/test_send_invitation_email.py | 61 +++-- tests/system/action/user/test_set_present.py | 49 ++++ .../user/test_toggle_presence_by_number.py | 54 ++++ tests/system/action/user/test_update.py | 248 ++++++++++++++++++ tests/system/presenter/test_export_meeting.py | 10 + .../test_get_forwarding_committees.py | 11 + .../presenter/test_get_forwarding_meetings.py | 11 + .../presenter/test_get_user_related_models.py | 80 ++++++ .../test_search_for_id_by_external_id.py | 74 ++++++ 168 files changed, 2256 insertions(+), 154 deletions(-) diff --git a/docs/actions/meeting.clone.md b/docs/actions/meeting.clone.md index 8d303a623..098c3e181 100644 --- a/docs/actions/meeting.clone.md +++ b/docs/actions/meeting.clone.md @@ -27,6 +27,8 @@ will be cloned untouched. It has to be checked, whether the organization.limit_of_meetings is unlimited(=0) or lower than the active meetings in organization.active_meeting_ids, if the new meeting is not archived (`is_active_in_organization_id` is set) +Meetings that have `locked_from_inside` set to true can not be cloned. + ### Pre Updating fields The fields `welcome_title, description, start_time, end_time, location, organization_tag_ids, name` could be updated for the diff --git a/docs/actions/meeting.import.md b/docs/actions/meeting.import.md index ce7495468..60a61cace 100644 --- a/docs/actions/meeting.import.md +++ b/docs/actions/meeting.import.md @@ -28,3 +28,4 @@ The user must be the committee manager of the given committee. ### Info The `meeting` object must contain a valid `_migration_index` on root level. +The `meeting` object cannot have `locked_from_inside` set to true. diff --git a/docs/actions/meeting.update.md b/docs/actions/meeting.update.md index 13b6c4e68..59197e6c9 100644 --- a/docs/actions/meeting.update.md +++ b/docs/actions/meeting.update.md @@ -14,6 +14,7 @@ location: string; start_time: timestamp; end_time: timestamp; + locked_from_inside: boolean; conference_show: boolean; conference_auto_connect: boolean; @@ -183,7 +184,6 @@ jitsi_domain: string; jitsi_room_name: string; jitsi_room_password: string; - enable_chat: boolean; } ``` @@ -193,6 +193,8 @@ Updates the meeting. If `set_as_template` is `True`, `template_for_organization_id` has to be set to `1`. If it is `False`, it has to be set to `None`. `reference_projector_id` can only be set to a projector, which is not internal. +This action doesn't allow for a meeting to be set as a template and have `locked_from_inside` set to true at the same time. if this would be the result of an action call, an exception will be thrown. + ## Permissions - Users with `meeting.can_manage_settings` can modify group A - Users with `user.can_update` can modify group B diff --git a/docs/actions/participant.json_upload.md b/docs/actions/participant.json_upload.md index 61a897802..1531a95d1 100644 --- a/docs/actions/participant.json_upload.md +++ b/docs/actions/participant.json_upload.md @@ -92,4 +92,4 @@ Permissions are analogue to `user.create` and `user.update`. The `saml_id` can b In case of an update, remove fields from the payload that don't change the content compared to database to avoid unnecessary permission errors. Don't forget the special permissions for `default_password` on `user.update`. -Anyway the user must have the permission `user.can_manage`, `cml.can_manage` or >= `oml.can_manage_users`. Otherwise the whole import will be finished with an exception. \ No newline at end of file +Anyway the user must have the permission `user.can_manage` or, if the meeting is not locked via the setting `locked_from_inside`, `cml.can_manage` or >= `oml.can_manage_users`. Otherwise the whole import will be finished with an exception. \ No newline at end of file diff --git a/docs/actions/user.assign_meetings.md b/docs/actions/user.assign_meetings.md index 4a785fd97..e41b85374 100644 --- a/docs/actions/user.assign_meetings.md +++ b/docs/actions/user.assign_meetings.md @@ -23,6 +23,8 @@ Go through all meetings: If it doesn't find the `group_name` in at least one meeting, throw an `ActionException`. Returns dictionary with `"succeeded": [meeting_ids], "standard_group": [meeting_ids], "nothing": [meeting_ids]`. +Will raise an error if some of the selected meetings are locked from the inside via the `locked_from_inside` setting. + ## Permissions The request user needs OML `can_manage_users` diff --git a/docs/actions/user.create.md b/docs/actions/user.create.md index 13bca4feb..d207715ac 100644 --- a/docs/actions/user.create.md +++ b/docs/actions/user.create.md @@ -89,10 +89,10 @@ The request user needs `user.can_manage` in the meeting of meeting_id. Group C: -The request user must satisfy at least one of: -- the OML `can_manage_users` -- For each meeting: - * `user.can_manage` for the meeting, OR +For each meeting the request user must satisfy at least one of: +- `user.can_manage` for the meeting, OR +- If the meeting is not locked via `locked_from_inside` setting: + * the OML `can_manage_users` in the organization * The CML `can_manage` for the committee of the meeting Group D: diff --git a/docs/actions/user.send_invitation_email.md b/docs/actions/user.send_invitation_email.md index ac7517436..a48b4e6a0 100644 --- a/docs/actions/user.send_invitation_email.md +++ b/docs/actions/user.send_invitation_email.md @@ -49,5 +49,4 @@ It does an equal string formatting as for the subject. Use `meeting/users_email Sending email is no longer refused, the wrong keyword will be injected instead. ## Permissions -The requesting user needs either the permission `user.can_update` (if a `meeting_id` is given) in -each referenced meeting or the permission OML `can_manage_users`. +The requesting user needs the permission `user.can_update` if a `meeting_id` is given or, if not, the permission OML `can_manage_users`. diff --git a/docs/actions/user.set_present.md b/docs/actions/user.set_present.md index 29f45494f..07d2e7e75 100644 --- a/docs/actions/user.set_present.md +++ b/docs/actions/user.set_present.md @@ -14,7 +14,8 @@ Sets the user's present status in the given meeting. If `present` is true, `meet ## Permissions One of the following has to be true: -* The request user has the OML `can_manage_users` -* The request user has the CML `can_manage` in the given meeting's committee +* The meeting is not locked via the setting `locked_from_inside` and: + * The request user has the OML `can_manage_users` + * The request user has the CML `can_manage` in the given meeting's committee * The request user has `user.can_update` or `user.can_manage_presence` in the given meeting * The `user_id` is equal to the request user id and the setting `users_allow_self_set_present` is set to `True` in the given meeting diff --git a/docs/actions/user.toggle_presence_by_number.md b/docs/actions/user.toggle_presence_by_number.md index d8b1f3585..ec6b2e703 100644 --- a/docs/actions/user.toggle_presence_by_number.md +++ b/docs/actions/user.toggle_presence_by_number.md @@ -29,6 +29,7 @@ If the action is successful, it returns the id of the modified user. Thus the ac ## Permission One of the following has to be true: -* The request user has the OML `can_manage_users` -* The request user has the CML `can_manage` in the given meeting's committee +* The meeting is not locked via the setting `locked_from_inside` and: + * The request user has the OML `can_manage_users` + * The request user has the CML `can_manage` in the given meeting's committee * The request user has `user.can_update` in the given meeting \ No newline at end of file diff --git a/docs/actions/user.update.md b/docs/actions/user.update.md index fa0ba8995..2b4858110 100644 --- a/docs/actions/user.update.md +++ b/docs/actions/user.update.md @@ -87,10 +87,11 @@ The request user needs `user.can_update` in each referenced meeting. Group C: -The request user must satisfy at least one of: -- the OML `can_manage_users` -- `user.can_update` for the meeting, OR -- The CML `can_manage` for the committee of the meeting +For each meeting the request user must satisfy at least one of: +- `user.can_manage` for the meeting, OR +- If the meeting is not locked via `locked_from_inside` setting: + * the OML `can_manage_users` in the organization + * The CML `can_manage` for the committee of the meeting Group D: diff --git a/docs/presenters/export_meeting.md b/docs/presenters/export_meeting.md index 967a7cc02..73e61cdac 100644 --- a/docs/presenters/export_meeting.md +++ b/docs/presenters/export_meeting.md @@ -14,6 +14,7 @@ JSON with export # Logic The presenter exports the meeting, the collections which belong to the meeting and users of the meeting. It uses the meeting.user_id for that. And it excludes the organization tags and the committee. +Will raise an exception if the meeting is locked voa the `locked_from_inside` setting. # Permissions The request user must have the `SUPERADMIN` organization management level. diff --git a/docs/presenters/get_user_related_models.md b/docs/presenters/get_user_related_models.md index 02b30b9aa..ea54c8993 100644 --- a/docs/presenters/get_user_related_models.md +++ b/docs/presenters/get_user_related_models.md @@ -17,8 +17,9 @@ id: Id; name: String; is_active_in_organization_id: Id; + is_locked: boolean; motion_submitter_ids: Id[]; - assignemnt_candidate_ids: Id[]; + assignment_candidate_ids: Id[]; speaker_ids: Id[]; }] } @@ -29,6 +30,7 @@ It iterates over the given `user_ids`. For every id of `user_ids` all objects are searched which are associated with that id. This means that for every committee it is checked if the user (specified by the id) is a manager or member of the committee, and for every meeting if the user is listed as a speaker of any `agenda_item` or as a submitter of any `motion` or as a candidate of any `assignment`. The result is a dictionary whose keys are the `user_ids`. The values are threefolded: `organization_management_level` contains the OML of the user. The two other values are arrays, one for the `committees` and one for the `meetings`. If a user is no member of any committee, then the `committees` array is empty and omitted. The same applies to the `meetings` array. +If a meeting has `locked_from_inside` set to true, `is_locked` will be true and `motion_submitter_ids`, `assignment_candidate_ids` and `speaker_ids` will be left out for this meeting, unless the calling user is in the meeting himself. Every committee is given by its name and id as well as the CML of the user (given by the `user_id`). Every meeting is given by its name, its id and its `is_active_in_organization_id` (to indicate if the meeting is archived). diff --git a/docs/presenters/search_for_id_by_external_id.md b/docs/presenters/search_for_id_by_external_id.md index d4a61573e..ebbd42a81 100644 --- a/docs/presenters/search_for_id_by_external_id.md +++ b/docs/presenters/search_for_id_by_external_id.md @@ -28,6 +28,8 @@ It should be search for an `external_id` in the given collection (`group`, `meet Following error cases could be encountered: "No item with 'external_id' was found" and "More then one item with 'external_id' were found". +If the `group` collection is given, and there are locked meetings, the presenter will act as if these groups do not exist. They will not count towards the result. + For searching a user by `saml_id` see [search_users](search_users.md). ## Permissions diff --git a/global/meta b/global/meta index 2e80fc018..956a00a9e 160000 --- a/global/meta +++ b/global/meta @@ -1 +1 @@ -Subproject commit 2e80fc0188c6d0288447c07458f15b031dd89ce5 +Subproject commit 956a00a9e997de81419242959602141a08030228 diff --git a/openslides_backend/action/actions/meeting/clone.py b/openslides_backend/action/actions/meeting/clone.py index d0d0d837e..cbd4a9354 100644 --- a/openslides_backend/action/actions/meeting/clone.py +++ b/openslides_backend/action/actions/meeting/clone.py @@ -96,6 +96,9 @@ def update_instance(self, instance: dict[str, Any]) -> dict[str, Any]: self.check_one_meeting(instance) meeting = self.get_meeting_from_json(meeting_json) + if meeting.get("locked_from_inside"): + raise ActionException("Cannot clone locked meeting.") + if committee_id := instance.get("committee_id"): meeting["committee_id"] = committee_id diff --git a/openslides_backend/action/actions/meeting/import_.py b/openslides_backend/action/actions/meeting/import_.py index 21624c0b6..f7840d384 100644 --- a/openslides_backend/action/actions/meeting/import_.py +++ b/openslides_backend/action/actions/meeting/import_.py @@ -145,6 +145,7 @@ def prefetch(self, action_data: ActionData) -> None: def preprocess_data(self, instance: dict[str, Any]) -> dict[str, Any]: self.check_one_meeting(instance) + self.check_locked(instance) self.remove_not_allowed_fields(instance) self.set_committee_and_orga_relation(instance) instance = self.migrate_data(instance) @@ -155,6 +156,10 @@ def check_one_meeting(self, instance: dict[str, Any]) -> None: if len(instance["meeting"]["meeting"]) != 1: raise ActionException("Need exactly one meeting in meeting collection.") + def check_locked(self, instance: dict[str, Any]) -> None: + if list(instance["meeting"]["meeting"].values())[0].get("locked_from_inside"): + raise ActionException("Cannot import a locked meeting.") + def remove_not_allowed_fields(self, instance: dict[str, Any]) -> None: json_data = instance["meeting"] diff --git a/openslides_backend/action/actions/meeting/update.py b/openslides_backend/action/actions/meeting/update.py index 595f41929..b9e2993ca 100644 --- a/openslides_backend/action/actions/meeting/update.py +++ b/openslides_backend/action/actions/meeting/update.py @@ -33,6 +33,7 @@ "location", "start_time", "end_time", + "locked_from_inside", "conference_show", "conference_auto_connect", "conference_los_restriction", @@ -208,6 +209,24 @@ def validate_instance(self, instance: dict[str, Any]) -> None: def update_instance(self, instance: dict[str, Any]) -> dict[str, Any]: # handle set_as_template set_as_template = instance.pop("set_as_template", None) + db_meeting = self.datastore.get( + fqid_from_collection_and_id("meeting", instance["id"]), + ["template_for_organization_id", "locked_from_inside"], + lock_result=False, + ) + lock_meeting = ( + instance.get("locked_from_inside") + if instance.get("locked_from_inside") is not None + else db_meeting.get("locked_from_inside") + ) + if lock_meeting and ( + set_as_template + if set_as_template is not None + else db_meeting.get("template_for_organization_id") + ): + raise ActionException( + "A meeting cannot be locked from the inside and a template at the same time." + ) if set_as_template is True: instance["template_for_organization_id"] = 1 elif set_as_template is False: diff --git a/openslides_backend/action/actions/user/assign_meetings.py b/openslides_backend/action/actions/user/assign_meetings.py index a1c423973..344890d35 100644 --- a/openslides_backend/action/actions/user/assign_meetings.py +++ b/openslides_backend/action/actions/user/assign_meetings.py @@ -2,6 +2,7 @@ from ....models.models import User from ....permissions.management_levels import OrganizationManagementLevel +from ....services.datastore.commands import GetManyRequest from ....shared.exceptions import ActionException from ....shared.filters import And, FilterOperator from ....shared.patterns import fqid_from_collection_and_id @@ -34,6 +35,7 @@ class UserAssignMeetings(MeetingUserHelperMixin, UpdateAction): use_meeting_ids_for_archived_meeting_check = True def update_instance(self, instance: dict[str, Any]) -> dict[str, Any]: + self.check_meetings(instance) user_id = instance["id"] meeting_ids = set(instance.pop("meeting_ids")) group_name = instance.pop("group_name") @@ -135,6 +137,23 @@ def update_instance(self, instance: dict[str, Any]) -> dict[str, Any]: return instance + def check_meetings(self, instance: dict[str, Any]) -> None: + if meeting_ids := instance.get("meeting_ids"): + locked_meetings = [ + str(id_) + for id_, meeting in self.datastore.get_many( + [GetManyRequest("meeting", meeting_ids, ["locked_from_inside"])], + lock_result=False, + ) + .get("meeting", {}) + .items() + if meeting.get("locked_from_inside") + ] + if len(locked_meetings): + raise ActionException( + f"Cannot assign meetings because some selected meetings are locked: {', '.join(locked_meetings)}." + ) + def create_action_result_element( self, instance: dict[str, Any] ) -> ActionResultElement | None: diff --git a/openslides_backend/action/actions/user/create_update_permissions_mixin.py b/openslides_backend/action/actions/user/create_update_permissions_mixin.py index 92c8fe24d..2ca55034e 100644 --- a/openslides_backend/action/actions/user/create_update_permissions_mixin.py +++ b/openslides_backend/action/actions/user/create_update_permissions_mixin.py @@ -221,8 +221,6 @@ def check_permissions(self, instance: dict[str, Any]) -> None: self.datastore, self.user_id, self.permission ) actual_group_fields = self._get_actual_grouping_from_instance(instance) - if self.permstore.user_oml == OrganizationManagementLevel.SUPERADMIN: - return # store scope, id and OML-permission for requested user ( @@ -232,14 +230,24 @@ def check_permissions(self, instance: dict[str, Any]) -> None: self.instance_committee_ids, ) = self.get_user_scope(instance.get("id") or instance) - self._check_for_higher_OML(actual_group_fields, instance) + if self.permstore.user_oml != OrganizationManagementLevel.SUPERADMIN: + self._check_for_higher_OML(actual_group_fields, instance) + + instance_meeting_id = instance.get("meeting_id") + locked_from_inside = False + if instance_meeting_id: + locked_from_inside = self.datastore.get( + fqid_from_collection_and_id("meeting", instance_meeting_id), + ["locked_from_inside"], + lock_result=False, + ).get("locked_from_inside", False) - # Ordered by supposed velocity advantages. Changing order only can effect the sequence of detected errors for tests + # Ordered by supposed velocity advantages. Changing order can only effect the sequence of detected errors for tests self.check_group_H(actual_group_fields["H"]) self.check_group_E(actual_group_fields["E"], instance) self.check_group_D(actual_group_fields["D"], instance) - self.check_group_C(actual_group_fields["C"], instance) - self.check_group_B(actual_group_fields["B"], instance) + self.check_group_C(actual_group_fields["C"], instance, locked_from_inside) + self.check_group_B(actual_group_fields["B"], instance, locked_from_inside) self.check_group_A(actual_group_fields["A"]) self.check_group_F(actual_group_fields["F"]) self.check_group_G(actual_group_fields["G"]) @@ -250,7 +258,8 @@ def check_group_A( ) -> None: """Check Group A: Depending on scope of user to act on""" if ( - not fields + self.permstore.user_oml == OrganizationManagementLevel.SUPERADMIN + or not fields or self.permstore.user_oml >= OrganizationManagementLevel.CAN_MANAGE_USERS ): return @@ -284,33 +293,42 @@ def check_group_A( } ) - def check_group_B(self, fields: list[str], instance: dict[str, Any]) -> None: + def check_group_B( + self, fields: list[str], instance: dict[str, Any], locked_from_inside: bool + ) -> None: """Check Group B meeting fields: Only meeting.permissions for each meeting""" - if fields: + if ( + self.permstore.user_oml != OrganizationManagementLevel.SUPERADMIN + or locked_from_inside + ) and fields: meeting_ids = self._meetings_from_group_B_fields_from_instance(instance) if diff := meeting_ids - self.permstore.user_meetings: raise MissingPermission( {self.permission: meeting_id for meeting_id in diff} ) - def check_group_C(self, fields: list[str], instance: dict[str, Any]) -> None: + def check_group_C( + self, fields: list[str], instance: dict[str, Any], locked_from_inside: bool + ) -> None: """Check Group C group_ids: OML, CML or meeting.permissions for each meeting""" if ( - fields - and self.permstore.user_oml < OrganizationManagementLevel.CAN_MANAGE_USERS - ): + (self.permstore.user_oml < OrganizationManagementLevel.CAN_MANAGE_USERS) + or locked_from_inside + ) and fields: touch_meeting_id = instance.get("meeting_id") if ( - touch_meeting_id not in self.permstore.user_committees_meetings - and touch_meeting_id not in self.permstore.user_meetings - ): + locked_from_inside + or touch_meeting_id not in self.permstore.user_committees_meetings + ) and touch_meeting_id not in self.permstore.user_meetings: raise PermissionDenied( - f"The user needs OrganizationManagementLevel.can_manage_users or CommitteeManagementLevel.can_manage for committee of following meeting or Permission {self.permission} for meeting {touch_meeting_id}" + f"The user needs Permission {self.permission} for meeting {touch_meeting_id}" + if locked_from_inside + else f"The user needs OrganizationManagementLevel.can_manage_users or CommitteeManagementLevel.can_manage for committee of following meeting or Permission {self.permission} for meeting {touch_meeting_id}" ) def check_group_D(self, fields: list[str], instance: dict[str, Any]) -> None: """Check Group D committee-related fields: OML or CML level for each committee""" - if ( + if self.permstore.user_oml != OrganizationManagementLevel.SUPERADMIN and ( fields and self.permstore.user_oml < OrganizationManagementLevel.CAN_MANAGE_USERS ): @@ -325,7 +343,7 @@ def check_group_D(self, fields: list[str], instance: dict[str, Any]) -> None: def check_group_E(self, fields: list[str], instance: dict[str, Any]) -> None: """Check Group E organization_management_level: OML level necessary""" - if fields: + if self.permstore.user_oml != OrganizationManagementLevel.SUPERADMIN and fields: expected_oml = max( OrganizationManagementLevel( instance.get("organization_management_level") @@ -343,7 +361,10 @@ def check_group_F( ) -> None: """Check F common fields: scoped permissions necessary, but if instance user has an oml-permission, that of the request user must be higher""" - if not fields: + if ( + self.permstore.user_oml == OrganizationManagementLevel.SUPERADMIN + or not fields + ): return if ( @@ -394,7 +415,11 @@ def check_group_F( def check_group_G(self, fields: list[str]) -> None: """Group G: OML SUPERADMIN necessary""" - if fields and self.permstore.user_oml < OrganizationManagementLevel.SUPERADMIN: + if ( + self.permstore.user_oml != OrganizationManagementLevel.SUPERADMIN + and fields + and self.permstore.user_oml < OrganizationManagementLevel.SUPERADMIN + ): raise MissingPermission(OrganizationManagementLevel.SUPERADMIN) def check_group_H( @@ -405,12 +430,16 @@ def check_group_H( Check Group H: Like group A, but only on internal calls, which will never call the check_permissions automatically or oml.can_manage_user permission in user.create """ - if fields and not ( - self.internal - or ( - self.name == "user.create" - and self.permstore.user_oml - >= OrganizationManagementLevel.CAN_MANAGE_USERS + if ( + self.permstore.user_oml != OrganizationManagementLevel.SUPERADMIN + and fields + and not ( + self.internal + or ( + self.name == "user.create" + and self.permstore.user_oml + >= OrganizationManagementLevel.CAN_MANAGE_USERS + ) ) ): msg = "The field 'saml_id' can only be used in internal action calls" @@ -539,6 +568,15 @@ def get_failing_fields(self, instance: dict[str, Any]) -> list[str]: self.instance_committee_ids, ) = self.get_user_scope(instance.get("id") or instance) + instance_meeting_id = instance.get("meeting_id") + locked_from_inside = False + if instance_meeting_id: + locked_from_inside = self.datastore.get( + fqid_from_collection_and_id("meeting", instance_meeting_id), + ["locked_from_inside"], + lock_result=False, + ).get("locked_from_inside", False) + actual_group_fields = self._get_actual_grouping_from_instance(instance) """ group[H] fields are internal, but generally allowed in import. @@ -546,21 +584,30 @@ def get_failing_fields(self, instance: dict[str, Any]) -> list[str]: if actual_group_fields["H"]: actual_group_fields["A"] += actual_group_fields["H"] failing_fields: list[str] = [] - for method, fields, inst_param in [ - (self.check_group_E, actual_group_fields["E"], instance), - (self.check_group_D, actual_group_fields["D"], instance), - (self.check_group_B, actual_group_fields["B"], instance), - (self.check_group_A, actual_group_fields["A"], None), - (self.check_group_F, actual_group_fields["F"], None), - (self.check_group_G, actual_group_fields["G"], None), + for method, fields, inst_param, other_param in [ + (self.check_group_E, actual_group_fields["E"], instance, None), + (self.check_group_D, actual_group_fields["D"], instance, None), + ( + self.check_group_B, + actual_group_fields["B"], + instance, + locked_from_inside, + ), + (self.check_group_A, actual_group_fields["A"], None, None), + (self.check_group_F, actual_group_fields["F"], None, None), + (self.check_group_G, actual_group_fields["G"], None, None), ]: try: if inst_param is None: cast(Callable[[list[str]], None], method)(fields) - else: + elif other_param is None: cast(Callable[[list[str], dict[str, Any]], None], method)( fields, inst_param ) + else: + cast(Callable[[list[str], dict[str, Any], bool], None], method)( + fields, inst_param, other_param + ) except PermissionDenied: failing_fields += fields return failing_fields diff --git a/openslides_backend/action/actions/user/participant_common.py b/openslides_backend/action/actions/user/participant_common.py index 97ebf0aee..7cd1090d3 100644 --- a/openslides_backend/action/actions/user/participant_common.py +++ b/openslides_backend/action/actions/user/participant_common.py @@ -20,23 +20,29 @@ class ParticipantCommon(BaseImportJsonUploadAction): def check_permissions(self, instance: dict[str, Any]) -> None: permstore = PermissionVarStore(self.datastore, self.user_id) - if ( - self.meeting_id not in permstore.user_meetings - and permstore.user_oml < OrganizationManagementLevel.CAN_MANAGE_USERS - and self.meeting_id not in permstore.user_committees_meetings - ): + if self.meeting_id not in permstore.user_meetings: meeting = self.datastore.get( fqid_from_collection_and_id("meeting", self.meeting_id), - ["committee_id"], + ["committee_id", "locked_from_inside"], lock_result=False, ) - raise MissingPermission( - { - Permissions.User.CAN_MANAGE: self.meeting_id, - OrganizationManagementLevel.CAN_MANAGE_ORGANIZATION: 1, - CommitteeManagementLevel.CAN_MANAGE: meeting["committee_id"], - } - ) + if meeting.get("locked_from_inside"): + raise MissingPermission( + { + Permissions.User.CAN_MANAGE: self.meeting_id, + } + ) + if ( + permstore.user_oml < OrganizationManagementLevel.CAN_MANAGE_USERS + and self.meeting_id not in permstore.user_committees_meetings + ): + raise MissingPermission( + { + Permissions.User.CAN_MANAGE: self.meeting_id, + OrganizationManagementLevel.CAN_MANAGE_ORGANIZATION: 1, + CommitteeManagementLevel.CAN_MANAGE: meeting["committee_id"], + } + ) self.permission_check = CreateUpdatePermissionsFailingFields( permstore, diff --git a/openslides_backend/action/actions/user/send_invitation_email.py b/openslides_backend/action/actions/user/send_invitation_email.py index e433dcba3..a2c995fd1 100644 --- a/openslides_backend/action/actions/user/send_invitation_email.py +++ b/openslides_backend/action/actions/user/send_invitation_email.py @@ -300,7 +300,7 @@ def check_permissions(self, instance: dict[str, Any]) -> None: instance["meeting_id"], ): return - if has_organization_management_level( + if not instance.get("meeting_id") and has_organization_management_level( self.datastore, self.user_id, OrganizationManagementLevel.CAN_MANAGE_USERS ): return diff --git a/openslides_backend/action/actions/user/set_present.py b/openslides_backend/action/actions/user/set_present.py index 462aeca17..632fd4688 100644 --- a/openslides_backend/action/actions/user/set_present.py +++ b/openslides_backend/action/actions/user/set_present.py @@ -64,10 +64,6 @@ def get_updated_instances(self, action_data: ActionData) -> ActionData: yield instance def check_permissions(self, instance: dict[str, Any]) -> None: - if has_organization_management_level( - self.datastore, self.user_id, OrganizationManagementLevel.CAN_MANAGE_USERS - ): - return if has_perm( self.datastore, self.user_id, @@ -77,16 +73,23 @@ def check_permissions(self, instance: dict[str, Any]) -> None: return meeting = self.datastore.get( fqid_from_collection_and_id("meeting", instance["meeting_id"]), - ["committee_id", "users_allow_self_set_present"], + ["committee_id", "users_allow_self_set_present", "locked_from_inside"], lock_result=False, ) - if has_committee_management_level( - self.datastore, - self.user_id, - CommitteeManagementLevel.CAN_MANAGE, - meeting["committee_id"], - ): - return + if not meeting.get("locked_from_inside"): + if has_organization_management_level( + self.datastore, + self.user_id, + OrganizationManagementLevel.CAN_MANAGE_USERS, + ): + return + if has_committee_management_level( + self.datastore, + self.user_id, + CommitteeManagementLevel.CAN_MANAGE, + meeting["committee_id"], + ): + return if self.user_id == instance["id"] and meeting.get( "users_allow_self_set_present" ): diff --git a/openslides_backend/action/actions/user/toggle_presence_by_number.py b/openslides_backend/action/actions/user/toggle_presence_by_number.py index 1cc076678..b9e90fd34 100644 --- a/openslides_backend/action/actions/user/toggle_presence_by_number.py +++ b/openslides_backend/action/actions/user/toggle_presence_by_number.py @@ -78,10 +78,6 @@ def create_action_result_element( return {"id": instance["id"]} def check_permissions(self, instance: dict[str, Any]) -> None: - if has_organization_management_level( - self.datastore, self.user_id, OrganizationManagementLevel.CAN_MANAGE_USERS - ): - return if has_perm( self.datastore, self.user_id, @@ -91,14 +87,21 @@ def check_permissions(self, instance: dict[str, Any]) -> None: return meeting = self.datastore.get( fqid_from_collection_and_id("meeting", instance["meeting_id"]), - ["committee_id"], + ["committee_id", "locked_from_inside"], lock_result=False, ) - if has_committee_management_level( - self.datastore, - self.user_id, - CommitteeManagementLevel.CAN_MANAGE, - meeting["committee_id"], - ): - return + if not meeting.get("locked_from_inside"): + if has_organization_management_level( + self.datastore, + self.user_id, + OrganizationManagementLevel.CAN_MANAGE_USERS, + ): + return + if has_committee_management_level( + self.datastore, + self.user_id, + CommitteeManagementLevel.CAN_MANAGE, + meeting["committee_id"], + ): + return raise PermissionDenied("You are not allowed to toggle presence by number.") diff --git a/openslides_backend/models/models.py b/openslides_backend/models/models.py index 70e0402cc..8676cdb6c 100644 --- a/openslides_backend/models/models.py +++ b/openslides_backend/models/models.py @@ -330,6 +330,7 @@ class Meeting(Model, MeetingModelMixin): location = fields.CharField() start_time = fields.TimestampField() end_time = fields.TimestampField() + locked_from_inside = fields.BooleanField() imported_at = fields.TimestampField() language = fields.CharField( required=True, diff --git a/openslides_backend/permissions/permission_helper.py b/openslides_backend/permissions/permission_helper.py index b216bf656..47a650780 100644 --- a/openslides_backend/permissions/permission_helper.py +++ b/openslides_backend/permissions/permission_helper.py @@ -13,31 +13,34 @@ def has_perm( datastore: DatastoreService, user_id: int, permission: Permission, meeting_id: int ) -> bool: + meeting = datastore.get( + fqid_from_collection_and_id("meeting", meeting_id), + ["default_group_id", "enable_anonymous", "locked_from_inside"], + lock_result=False, + ) + not_locked_from_editing = not meeting.get("locked_from_inside") # anonymous cannot be fetched from db if user_id > 0: - user = datastore.get( - fqid_from_collection_and_id("user", user_id), - [ - "organization_management_level", - ], - lock_result=False, - ) - # superadmins have all permissions - if ( - user.get("organization_management_level") - == OrganizationManagementLevel.SUPERADMIN - ): - return True + # superadmins have all permissions if the meeting isn't locked from the inside + if not_locked_from_editing: + user = datastore.get( + fqid_from_collection_and_id("user", user_id), + [ + "organization_management_level", + ], + lock_result=False, + ) + if ( + user.get("organization_management_level") + == OrganizationManagementLevel.SUPERADMIN + ): + return True group_ids = get_groups_from_meeting_user(datastore, meeting_id, user_id) if not group_ids: return False elif user_id == 0: # anonymous users are in the default group - meeting = datastore.get( - fqid_from_collection_and_id("meeting", meeting_id), - ["default_group_id", "enable_anonymous"], - ) # check if anonymous is allowed if not meeting.get("enable_anonymous"): raise PermissionDenied(f"Anonymous is not enabled for meeting {meeting_id}") @@ -161,14 +164,15 @@ def filter_surplus_permissions(permission_list: list[Permission]) -> list[Permis def is_admin(datastore: DatastoreService, user_id: int, meeting_id: int) -> bool: - if has_organization_management_level( + meeting = datastore.get( + fqid_from_collection_and_id("meeting", meeting_id), + ["admin_group_id", "locked_from_inside"], + lock_result=False, + ) + if not meeting.get("locked_from_inside") and has_organization_management_level( datastore, user_id, OrganizationManagementLevel.SUPERADMIN ): return True - meeting = datastore.get( - fqid_from_collection_and_id("meeting", meeting_id), - ["admin_group_id"], - ) group_ids = get_groups_from_meeting_user(datastore, meeting_id, user_id) return bool(group_ids) and meeting["admin_group_id"] in group_ids diff --git a/openslides_backend/presenter/export_meeting.py b/openslides_backend/presenter/export_meeting.py index ef63d6226..a6df59c5b 100644 --- a/openslides_backend/presenter/export_meeting.py +++ b/openslides_backend/presenter/export_meeting.py @@ -4,7 +4,7 @@ from ..permissions.management_levels import OrganizationManagementLevel from ..permissions.permission_helper import has_organization_management_level -from ..shared.exceptions import PermissionDenied +from ..shared.exceptions import PermissionDenied, PresenterException from ..shared.export_helper import export_meeting from ..shared.schema import required_id_schema, schema_version from .base import BasePresenter @@ -41,6 +41,15 @@ def get_result(self) -> Any: msg += f" Missing permission: {OrganizationManagementLevel.SUPERADMIN}" raise PermissionDenied(msg) export_data = export_meeting(self.datastore, self.data["meeting_id"]) + if id_ := next( + ( + id_ + for id_, meeting in export_data["meeting"].items() + if meeting.get("locked_from_inside") + ), + None, + ): + raise PresenterException(f"Cannot export: meeting {id_} is locked.") self.exclude_organization_tags_and_default_meeting_for_committee(export_data) return export_data diff --git a/openslides_backend/presenter/get_user_related_models.py b/openslides_backend/presenter/get_user_related_models.py index 7ab160ffe..0d0c1de7b 100644 --- a/openslides_backend/presenter/get_user_related_models.py +++ b/openslides_backend/presenter/get_user_related_models.py @@ -7,6 +7,7 @@ from ..services.datastore.commands import GetManyRequest from ..shared.exceptions import PresenterException +from ..shared.patterns import fqid_from_collection_and_id from ..shared.schema import schema_version from .base import BasePresenter from .presenter import register_presenter @@ -117,9 +118,15 @@ def get_meetings_data(self, user: dict[str, Any]) -> list[dict[str, Any]]: gmr = GetManyRequest( "meeting", [meeting_user["meeting_id"] for meeting_user in meeting_users], - ["id", "name", "is_active_in_organization_id"], + ["id", "name", "is_active_in_organization_id", "locked_from_inside"], ) meetings = self.datastore.get_many([gmr]).get("meeting", {}) + operator_meetings = self.datastore.get( + fqid_from_collection_and_id("user", self.user_id), + ["meeting_ids"], + lock_result=False, + ).get("meeting_ids", []) + return [ { "id": meeting["id"], @@ -127,10 +134,15 @@ def get_meetings_data(self, user: dict[str, Any]) -> list[dict[str, Any]]: "is_active_in_organization_id": meeting.get( "is_active_in_organization_id" ), + "is_locked": meeting.get("locked_from_inside", False), **{ field: value for field in result_fields if (value := meeting_user.get(field)) + and ( + not meeting.get("locked_from_inside") + or meeting["id"] in operator_meetings + ) }, } for meeting_user in meeting_users diff --git a/openslides_backend/presenter/search_for_id_by_external_id.py b/openslides_backend/presenter/search_for_id_by_external_id.py index 540e38f2e..6e4b985ee 100644 --- a/openslides_backend/presenter/search_for_id_by_external_id.py +++ b/openslides_backend/presenter/search_for_id_by_external_id.py @@ -2,8 +2,13 @@ import fastjsonschema +from openslides_backend.action.mixins.meeting_user_helper import ( + get_groups_from_meeting_user, +) + from ..permissions.management_levels import OrganizationManagementLevel from ..permissions.permission_helper import has_organization_management_level +from ..services.datastore.commands import GetManyRequest from ..shared.exceptions import MissingPermission from ..shared.filters import And, FilterOperator from ..shared.schema import schema_version @@ -52,17 +57,56 @@ def get_result(self) -> Any: context_field_map[self.data["collection"]], "=", self.data["context_id"] ), ) + mapped_fields = ["id"] + if is_group := self.data["collection"] == "group": + mapped_fields.append("meeting_id") filtered = self.datastore.filter( - self.data["collection"], filter_, ["id"] - ).values() + self.data["collection"], filter_, mapped_fields + ) + if is_group and len(filtered): + self.filter_out_locked_meeting_groups(filtered) if len(filtered) == 1: - return {"id": next(iter(filtered))["id"]} + return {"id": next(iter(filtered.values()))["id"]} elif len(filtered) == 0: error = f"No item with '{self.data['external_id']}' was found." else: error = f"More then one item with '{self.data['external_id']}' were found." return {"id": None, "error": error} + def filter_out_locked_meeting_groups( + self, filtered: dict[int, dict[str, Any]] + ) -> None: + remove_group_ids: list[int] = [] + meetings = self.datastore.get_many( + [ + GetManyRequest( + "meeting", + list( + { + meeting_id + for group in filtered.values() + if (meeting_id := group.get("meeting_id")) + } + ), + ["locked_from_inside", "group_ids"], + ) + ], + lock_result=False, + )["meeting"] + for group_id, group in filtered.items(): + if meetings.get(group.get("meeting_id", 0), {}).get( + "locked_from_inside" + ) and not set( + get_groups_from_meeting_user( + self.datastore, group["meeting_id"], self.user_id + ) + ).intersection( + meetings[group["meeting_id"]].get("group_ids", []) + ): + remove_group_ids.append(group_id) + for group_id in remove_group_ids: + del filtered[group_id] + def check_permissions(self) -> None: if not has_organization_management_level( self.datastore, diff --git a/tests/system/action/agenda_item/test_assign.py b/tests/system/action/agenda_item/test_assign.py index dcca17e08..e1fc1f7d1 100644 --- a/tests/system/action/agenda_item/test_assign.py +++ b/tests/system/action/agenda_item/test_assign.py @@ -1,3 +1,4 @@ +from openslides_backend.permissions.management_levels import OrganizationManagementLevel from openslides_backend.permissions.permissions import Permissions from tests.system.action.base import BaseActionTestCase from tests.system.util import CountDatastoreCalls @@ -85,7 +86,7 @@ def test_assign_parent_set(self) -> None: "agenda_item.assign", {"meeting_id": 222, "ids": [8, 9], "parent_id": 7} ) self.assert_status_code(response, 200) - assert counter.calls == 3 + assert counter.calls == 4 agenda_item_7 = self.get_model("agenda_item/7") assert agenda_item_7.get("child_ids") == [8, 9] assert agenda_item_7.get("parent_id") is None @@ -141,3 +142,16 @@ def test_assign_permissions(self) -> None: {"meeting_id": 1, "ids": [8], "parent_id": 7}, Permissions.AgendaItem.CAN_MANAGE, ) + + def test_assign_permissions_with_locked_meeting(self) -> None: + self.base_permission_test( + { + "agenda_item/7": {"meeting_id": 1}, + "agenda_item/8": {"meeting_id": 1}, + }, + "agenda_item.assign", + {"meeting_id": 1, "ids": [8], "parent_id": 7}, + OrganizationManagementLevel.SUPERADMIN, + True, + lock_meeting=True, + ) diff --git a/tests/system/action/agenda_item/test_create.py b/tests/system/action/agenda_item/test_create.py index d5ee99f2e..700fc7a98 100644 --- a/tests/system/action/agenda_item/test_create.py +++ b/tests/system/action/agenda_item/test_create.py @@ -326,6 +326,13 @@ def test_create_permissions(self) -> None: Permissions.AgendaItem.CAN_MANAGE, ) + def test_create_permissions_with_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + {"topic/1": {"meeting_id": 1}}, + "agenda_item.create", + {"content_object_id": "topic/1"}, + ) + def test_create_moderator_notes_no_permissions(self) -> None: self.base_permission_test( {"topic/1": {"meeting_id": 1}}, diff --git a/tests/system/action/agenda_item/test_delete.py b/tests/system/action/agenda_item/test_delete.py index e1cb3f525..646ba029a 100644 --- a/tests/system/action/agenda_item/test_delete.py +++ b/tests/system/action/agenda_item/test_delete.py @@ -102,3 +102,13 @@ def test_delete_permissions(self) -> None: {"id": 111}, Permissions.AgendaItem.CAN_MANAGE, ) + + def test_delete_permissions_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + { + "motion/34": {"agenda_item_id": 111, "meeting_id": 1}, + "agenda_item/111": {"content_object_id": "motion/34", "meeting_id": 1}, + }, + "agenda_item.delete", + {"id": 111}, + ) diff --git a/tests/system/action/agenda_item/test_numbering.py b/tests/system/action/agenda_item/test_numbering.py index c1f6d340c..1f8a68820 100644 --- a/tests/system/action/agenda_item/test_numbering.py +++ b/tests/system/action/agenda_item/test_numbering.py @@ -211,3 +211,25 @@ def test_numbering_permissions(self) -> None: {"meeting_id": 1}, Permissions.AgendaItem.CAN_MANAGE, ) + + def test_numbering_permissions_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + { + "meeting/1": { + "agenda_item_ids": [1, 2], + "is_active_in_organization_id": 1, + }, + "agenda_item/1": { + "meeting_id": 1, + "weight": 10, + "type": AgendaItem.AGENDA_ITEM, + }, + "agenda_item/2": { + "meeting_id": 1, + "weight": 10, + "type": AgendaItem.AGENDA_ITEM, + }, + }, + "agenda_item.numbering", + {"meeting_id": 1}, + ) diff --git a/tests/system/action/agenda_item/test_sort.py b/tests/system/action/agenda_item/test_sort.py index 5af949d6c..c41ef928f 100644 --- a/tests/system/action/agenda_item/test_sort.py +++ b/tests/system/action/agenda_item/test_sort.py @@ -186,3 +186,12 @@ def test_sort_permissions(self) -> None: {"meeting_id": 1, "tree": [{"id": 22}]}, Permissions.AgendaItem.CAN_MANAGE, ) + + def test_sort_permissions_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + { + "agenda_item/22": {"meeting_id": 1, "comment": "test1"}, + }, + "agenda_item.sort", + {"meeting_id": 1, "tree": [{"id": 22}]}, + ) diff --git a/tests/system/action/agenda_item/test_update.py b/tests/system/action/agenda_item/test_update.py index 3ae6eda2b..67233bb53 100644 --- a/tests/system/action/agenda_item/test_update.py +++ b/tests/system/action/agenda_item/test_update.py @@ -220,6 +220,13 @@ def test_update_permissions(self) -> None: Permissions.AgendaItem.CAN_MANAGE, ) + def test_update_permissions_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + {}, + "agenda_item.update", + {"id": 111, "duration": 1200}, + ) + def test_update_moderator_notes_no_permissions(self) -> None: self.base_permission_test( {}, diff --git a/tests/system/action/assignment/test_create.py b/tests/system/action/assignment/test_create.py index 1c30f3f61..ff56a9235 100644 --- a/tests/system/action/assignment/test_create.py +++ b/tests/system/action/assignment/test_create.py @@ -192,3 +192,13 @@ def test_create_permission(self) -> None: }, Permissions.Assignment.CAN_MANAGE, ) + + def test_create_permission_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + {}, + "assignment.create", + { + "title": "title_Xcdfgee", + "meeting_id": 1, + }, + ) diff --git a/tests/system/action/assignment/test_delete.py b/tests/system/action/assignment/test_delete.py index 744d552ba..0d4bc7c40 100644 --- a/tests/system/action/assignment/test_delete.py +++ b/tests/system/action/assignment/test_delete.py @@ -85,3 +85,12 @@ def test_delete_permission(self) -> None: {"id": 111}, Permissions.Assignment.CAN_MANAGE, ) + + def test_delete_permission_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + { + "assignment/111": {"meeting_id": 1, "title": "title_srtgb123"}, + }, + "assignment.delete", + {"id": 111}, + ) diff --git a/tests/system/action/assignment/test_update.py b/tests/system/action/assignment/test_update.py index 5ae9287c2..10cb247b3 100644 --- a/tests/system/action/assignment/test_update.py +++ b/tests/system/action/assignment/test_update.py @@ -78,3 +78,12 @@ def test_update_permission(self) -> None: {"id": 111, "title": "title_Xcdfgee"}, Permissions.Assignment.CAN_MANAGE, ) + + def test_update_permission_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + { + "assignment/111": {"title": "title_srtgb123", "meeting_id": 1}, + }, + "assignment.update", + {"id": 111, "title": "title_Xcdfgee"}, + ) diff --git a/tests/system/action/assignment_candidate/test_create.py b/tests/system/action/assignment_candidate/test_create.py index b4778224b..0838f5372 100644 --- a/tests/system/action/assignment_candidate/test_create.py +++ b/tests/system/action/assignment_candidate/test_create.py @@ -50,7 +50,7 @@ def test_create(self) -> None: {"assignment_id": 111, "meeting_user_id": 110}, ) self.assert_status_code(response, 200) - assert counter.calls == 6 + assert counter.calls == 7 model = self.get_model("assignment_candidate/1") assert model.get("meeting_user_id") == 110 assert model.get("assignment_id") == 111 diff --git a/tests/system/action/assignment_candidate/test_sort.py b/tests/system/action/assignment_candidate/test_sort.py index f2b793c5b..ad411f08f 100644 --- a/tests/system/action/assignment_candidate/test_sort.py +++ b/tests/system/action/assignment_candidate/test_sort.py @@ -170,3 +170,10 @@ def test_create_permissions(self) -> None: {"assignment_id": 222, "candidate_ids": [32, 31]}, Permissions.Assignment.CAN_MANAGE, ) + + def test_create_permissions_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + self.permission_test_models, + "assignment_candidate.sort", + {"assignment_id": 222, "candidate_ids": [32, 31]}, + ) diff --git a/tests/system/action/base.py b/tests/system/action/base.py index 67ca5825b..783948688 100644 --- a/tests/system/action/base.py +++ b/tests/system/action/base.py @@ -436,12 +436,15 @@ def base_permission_test( Permission | list[Permission] | OrganizationManagementLevel | None ) = None, fail: bool | None = None, + lock_meeting: bool = False, ) -> None: self.create_meeting() self.user_id = self.create_user("user") self.login(self.user_id) if models: self.set_models(models) + if lock_meeting: + self.set_models({"meeting/1": {"locked_from_inside": True}}) self.set_user_groups(self.user_id, [3]) if permission: if isinstance(permission, OrganizationManagementLevel): @@ -462,6 +465,21 @@ def base_permission_test( else: self.assert_status_code(response, 200) + def base_locked_out_superadmin_permission_test( + self, + models: dict[str, dict[str, Any]], + action: str, + action_data: dict[str, Any], + ) -> None: + self.base_permission_test( + models, + action, + action_data, + OrganizationManagementLevel.SUPERADMIN, + True, + True, + ) + @with_database_context def assert_history_information( self, fqid: FullQualifiedId, information: list[str] | None diff --git a/tests/system/action/base_motion_meeting_user_create_test.py b/tests/system/action/base_motion_meeting_user_create_test.py index 9037efaab..38d66a717 100644 --- a/tests/system/action/base_motion_meeting_user_create_test.py +++ b/tests/system/action/base_motion_meeting_user_create_test.py @@ -211,4 +211,11 @@ def test_create_permissions(self) -> None: Permissions.Motion.CAN_MANAGE_METADATA, ) + def test_create_permissions_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + self.permission_test_models, + self.action, + {"motion_id": 357, "meeting_user_id": 78}, + ) + return BaseMotionMeetingUserCreateTest diff --git a/tests/system/action/base_motion_meeting_user_delete_test.py b/tests/system/action/base_motion_meeting_user_delete_test.py index e58a53966..df67584f0 100644 --- a/tests/system/action/base_motion_meeting_user_delete_test.py +++ b/tests/system/action/base_motion_meeting_user_delete_test.py @@ -72,4 +72,11 @@ def test_delete_permissions(self) -> None: Permissions.Motion.CAN_MANAGE_METADATA, ) + def test_delete_permissions_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + self.permission_test_models, + self.action, + {"id": 111}, + ) + return BaseMotionMeetingUserDeleteTest diff --git a/tests/system/action/base_motion_meeting_user_sort_test.py b/tests/system/action/base_motion_meeting_user_sort_test.py index 752246f6c..a8753206b 100644 --- a/tests/system/action/base_motion_meeting_user_sort_test.py +++ b/tests/system/action/base_motion_meeting_user_sort_test.py @@ -89,4 +89,11 @@ def test_sort_permissions(self) -> None: Permissions.Motion.CAN_MANAGE_METADATA, ) + def test_sort_permissions_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + self.permission_test_models, + self.action, + {"motion_id": 222, f"{collection}_ids": [32, 31]}, + ) + return BaseMotionMeetingUserSortTest diff --git a/tests/system/action/chat_group/test_clear.py b/tests/system/action/chat_group/test_clear.py index c803cae76..56f9434c3 100644 --- a/tests/system/action/chat_group/test_clear.py +++ b/tests/system/action/chat_group/test_clear.py @@ -43,5 +43,12 @@ def test_clear_permission(self) -> None: self.assert_model_deleted("chat_message/113") self.assert_model_exists("chat_group/11", {"chat_message_ids": []}) + def test_clear_permission_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + self.test_models, + "chat_group.clear", + {"id": 11}, + ) + def test_clear_no_permission(self) -> None: self.base_permission_test(self.test_models, "chat_group.clear", {"id": 11}) diff --git a/tests/system/action/chat_group/test_create.py b/tests/system/action/chat_group/test_create.py index 904e36b76..3d1de4b00 100644 --- a/tests/system/action/chat_group/test_create.py +++ b/tests/system/action/chat_group/test_create.py @@ -128,6 +128,16 @@ def test_create_permissions(self) -> None: Permissions.Chat.CAN_MANAGE, ) + def test_create_permissions_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + { + ONE_ORGANIZATION_FQID: {"enable_chat": True}, + "meeting/1": {"name": "test"}, + }, + "chat_group.create", + {"name": "redekreis1", "meeting_id": 1}, + ) + def test_create_not_unique_name(self) -> None: self.set_models( { diff --git a/tests/system/action/chat_group/test_delete.py b/tests/system/action/chat_group/test_delete.py index d883c56bb..4d8b11bb0 100644 --- a/tests/system/action/chat_group/test_delete.py +++ b/tests/system/action/chat_group/test_delete.py @@ -37,3 +37,10 @@ def test_delete_permissions(self) -> None: {"id": 1}, Permissions.Chat.CAN_MANAGE, ) + + def test_delete_permissions_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + self.test_models, + "chat_group.delete", + {"id": 1}, + ) diff --git a/tests/system/action/chat_group/test_sort.py b/tests/system/action/chat_group/test_sort.py index f97d7c298..201b414e6 100644 --- a/tests/system/action/chat_group/test_sort.py +++ b/tests/system/action/chat_group/test_sort.py @@ -140,3 +140,10 @@ def test_sort_permissions(self) -> None: {"meeting_id": 1, "chat_group_ids": [32, 31]}, Permissions.Chat.CAN_MANAGE, ) + + def test_sort_permissions_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + self.permission_test_models, + "chat_group.sort", + {"meeting_id": 1, "chat_group_ids": [32, 31]}, + ) diff --git a/tests/system/action/chat_group/test_update.py b/tests/system/action/chat_group/test_update.py index e4be22b32..6c6ea7a55 100644 --- a/tests/system/action/chat_group/test_update.py +++ b/tests/system/action/chat_group/test_update.py @@ -82,6 +82,13 @@ def test_update_permissions(self) -> None: Permissions.Chat.CAN_MANAGE, ) + def test_update_permissions_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + self.test_models, + "chat_group.update", + {"id": 1, "name": "test"}, + ) + def test_update_not_unique_name(self) -> None: self.set_models( { diff --git a/tests/system/action/chat_message/test_delete.py b/tests/system/action/chat_message/test_delete.py index df8eeb48a..db449872a 100644 --- a/tests/system/action/chat_message/test_delete.py +++ b/tests/system/action/chat_message/test_delete.py @@ -47,3 +47,10 @@ def test_delete_correct_permission(self) -> None: Permissions.Chat.CAN_MANAGE, ) self.assert_model_deleted("chat_message/101") + + def test_delete_correct_permission_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + self.test_models, + "chat_message.delete", + {"id": 101}, + ) diff --git a/tests/system/action/group/test_create.py b/tests/system/action/group/test_create.py index 89c119bc7..ec765ee62 100644 --- a/tests/system/action/group/test_create.py +++ b/tests/system/action/group/test_create.py @@ -183,6 +183,13 @@ def test_create_check_permission(self) -> None: Permissions.User.CAN_MANAGE, ) + def test_create_check_permission_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + {}, + "group.create", + {"name": "test_Xcdfgee", "meeting_id": 1}, + ) + def test_create_external_id_forbidden(self) -> None: self.set_models( { diff --git a/tests/system/action/group/test_delete.py b/tests/system/action/group/test_delete.py index c2d62b455..4d23c96ce 100644 --- a/tests/system/action/group/test_delete.py +++ b/tests/system/action/group/test_delete.py @@ -112,6 +112,15 @@ def test_delete_permissions(self) -> None: Permissions.User.CAN_MANAGE, ) + def test_delete_permissions_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + { + "group/111": {"name": "name_srtgb123", "meeting_id": 1}, + }, + "group.delete", + {"id": 111}, + ) + def test_delete_mediafile1(self) -> None: self.set_models( { diff --git a/tests/system/action/group/test_update.py b/tests/system/action/group/test_update.py index 0f15cd8b9..400fa45df 100644 --- a/tests/system/action/group/test_update.py +++ b/tests/system/action/group/test_update.py @@ -102,3 +102,10 @@ def test_update_allowed(self) -> None: {"id": 3, "name": "name_Xcdfgee"}, Permissions.User.CAN_MANAGE, ) + + def test_update_permission_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + {}, + "group.update", + {"id": 3, "name": "name_Xcdfgee"}, + ) diff --git a/tests/system/action/list_of_speakers/test_delete_all_speakers.py b/tests/system/action/list_of_speakers/test_delete_all_speakers.py index db71c885e..f5dbe18a1 100644 --- a/tests/system/action/list_of_speakers/test_delete_all_speakers.py +++ b/tests/system/action/list_of_speakers/test_delete_all_speakers.py @@ -63,3 +63,10 @@ def test_delete_all_permissions(self) -> None: {"id": 111}, Permissions.ListOfSpeakers.CAN_MANAGE, ) + + def test_delete_all_permissions_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + self.permission_test_models, + "list_of_speakers.delete_all_speakers", + {"id": 111}, + ) diff --git a/tests/system/action/list_of_speakers/test_re_add_last.py b/tests/system/action/list_of_speakers/test_re_add_last.py index f45b8ebe5..7877c97f5 100644 --- a/tests/system/action/list_of_speakers/test_re_add_last.py +++ b/tests/system/action/list_of_speakers/test_re_add_last.py @@ -389,3 +389,10 @@ def test_re_add_last_permissions(self) -> None: {"id": 111}, Permissions.ListOfSpeakers.CAN_MANAGE, ) + + def test_re_add_last_permissions_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + {}, + "list_of_speakers.re_add_last", + {"id": 111}, + ) diff --git a/tests/system/action/list_of_speakers/test_update.py b/tests/system/action/list_of_speakers/test_update.py index c9c1da79f..b334eb7e0 100644 --- a/tests/system/action/list_of_speakers/test_update.py +++ b/tests/system/action/list_of_speakers/test_update.py @@ -57,3 +57,10 @@ def test_update_permissions(self) -> None: {"id": 111, "closed": True}, Permissions.ListOfSpeakers.CAN_MANAGE, ) + + def test_update_permissions_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + self.permission_test_models, + "list_of_speakers.update", + {"id": 111, "closed": True}, + ) diff --git a/tests/system/action/mediafile/test_create_directory.py b/tests/system/action/mediafile/test_create_directory.py index 27cc5723c..9827587e3 100644 --- a/tests/system/action/mediafile/test_create_directory.py +++ b/tests/system/action/mediafile/test_create_directory.py @@ -538,6 +538,17 @@ def test_create_directory_permissions(self) -> None: Permissions.Mediafile.CAN_MANAGE, ) + def test_create_directory_permissions_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + self.permission_test_models, + "mediafile.create_directory", + { + "owner_id": "meeting/1", + "title": "title_Xcdfgee", + "access_group_ids": [7], + }, + ) + def test_create_directory_no_permissions_orga_owner(self) -> None: self.base_permission_test( self.permission_test_models, diff --git a/tests/system/action/mediafile/test_delete.py b/tests/system/action/mediafile/test_delete.py index 877bf7f56..c2b8454cc 100644 --- a/tests/system/action/mediafile/test_delete.py +++ b/tests/system/action/mediafile/test_delete.py @@ -210,6 +210,13 @@ def test_delete_permissions(self) -> None: Permissions.Mediafile.CAN_MANAGE, ) + def test_delete_permissions_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + self.permission_test_models, + "mediafile.delete", + {"id": 222}, + ) + def test_delete_orga_no_permissions(self) -> None: self.permission_test_models["mediafile/222"]["owner_id"] = ONE_ORGANIZATION_FQID self.base_permission_test( diff --git a/tests/system/action/mediafile/test_move.py b/tests/system/action/mediafile/test_move.py index 05d40cb3d..cab160866 100644 --- a/tests/system/action/mediafile/test_move.py +++ b/tests/system/action/mediafile/test_move.py @@ -227,6 +227,13 @@ def test_move_permissions(self) -> None: Permissions.Mediafile.CAN_MANAGE, ) + def test_move_permissions_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + self.permission_test_models, + "mediafile.move", + {"owner_id": "meeting/1", "ids": [8], "parent_id": 7}, + ) + def test_move_no_permissions_orga(self) -> None: self.permission_test_models["mediafile/7"]["owner_id"] = ONE_ORGANIZATION_FQID self.permission_test_models["mediafile/8"]["owner_id"] = ONE_ORGANIZATION_FQID diff --git a/tests/system/action/mediafile/test_update.py b/tests/system/action/mediafile/test_update.py index f7b41fc3b..9f0e310de 100644 --- a/tests/system/action/mediafile/test_update.py +++ b/tests/system/action/mediafile/test_update.py @@ -695,6 +695,13 @@ def test_update_permissions(self) -> None: Permissions.Mediafile.CAN_MANAGE, ) + def test_update_permissions_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + self.permission_test_models, + "mediafile.update", + {"id": 111, "title": "title_Xcdfgee", "access_group_ids": [7]}, + ) + def test_update_no_permissions_orga_owner(self) -> None: self.permission_test_models["mediafile/111"]["owner_id"] = ONE_ORGANIZATION_FQID self.base_permission_test( diff --git a/tests/system/action/mediafile/test_upload.py b/tests/system/action/mediafile/test_upload.py index 7feb1e658..115c9165f 100644 --- a/tests/system/action/mediafile/test_upload.py +++ b/tests/system/action/mediafile/test_upload.py @@ -576,6 +576,18 @@ def test_upload_permissions(self) -> None: Permissions.Mediafile.CAN_MANAGE, ) + def test_upload_permissions_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + {}, + "mediafile.upload", + { + "title": "title_xXRGTLAJ", + "owner_id": "meeting/1", + "filename": "fn_jumbo.txt", + "file": base64.b64encode(b"testtesttest").decode(), + }, + ) + def test_upload_orga_owner_no_permissions(self) -> None: self.base_permission_test( {}, diff --git a/tests/system/action/meeting/test_archive.py b/tests/system/action/meeting/test_archive.py index bcf59b317..ba9687a25 100644 --- a/tests/system/action/meeting/test_archive.py +++ b/tests/system/action/meeting/test_archive.py @@ -71,6 +71,11 @@ def test_archive_permission_cml(self) -> None: response = self.request("meeting.archive", {"id": 1}) self.assert_status_code(response, 200) + def test_archive_locked_meeting(self) -> None: + self.set_models({"meeting/1": {"locked_from_inside": True}}) + response = self.request("meeting.archive", {"id": 1}) + self.assert_status_code(response, 200) + def test_archive_permission_oml(self) -> None: self.set_models( { diff --git a/tests/system/action/meeting/test_clone.py b/tests/system/action/meeting/test_clone.py index 19946e844..918ef6895 100644 --- a/tests/system/action/meeting/test_clone.py +++ b/tests/system/action/meeting/test_clone.py @@ -1900,3 +1900,10 @@ def test_clone_amendment_paragraphs(self) -> None: "motion/1/amendment_paragraphs error: Invalid html in 1\n\tmotion/1/amendment_paragraphs error: Invalid html in 2" in response.json["message"] ) + + def test_permissions_oml_locked_meeting(self) -> None: + self.create_meeting() + self.set_models({"meeting/1": {"locked_from_inside": True}}) + response = self.request("meeting.clone", {"meeting_id": 1, "committee_id": 2}) + self.assert_status_code(response, 400) + assert "Cannot clone locked meeting." in response.json["message"] diff --git a/tests/system/action/meeting/test_delete.py b/tests/system/action/meeting/test_delete.py index 4c28f18fe..9f78e5f5e 100644 --- a/tests/system/action/meeting/test_delete.py +++ b/tests/system/action/meeting/test_delete.py @@ -1,5 +1,6 @@ from typing import Any +from openslides_backend.permissions.management_levels import OrganizationManagementLevel from openslides_backend.shared.util import ONE_ORGANIZATION_FQID from tests.system.action.base import BaseActionTestCase @@ -415,3 +416,13 @@ def test_delete_with_poll_candidates_and_speakers(self) -> None: self.assert_model_deleted(fqid) for i in range(220, 222): self.assert_model_exists(f"user/{i}", {}) + + def test_delete_with_locked_meeting(self) -> None: + self.base_permission_test( + {}, + "meeting.delete", + {"id": 1}, + OrganizationManagementLevel.SUPERADMIN, + False, + lock_meeting=True, + ) diff --git a/tests/system/action/meeting/test_delete_all_speakers_of_all_lists.py b/tests/system/action/meeting/test_delete_all_speakers_of_all_lists.py index 1173cc9dc..d904774df 100644 --- a/tests/system/action/meeting/test_delete_all_speakers_of_all_lists.py +++ b/tests/system/action/meeting/test_delete_all_speakers_of_all_lists.py @@ -118,3 +118,10 @@ def test_permissions(self) -> None: {"id": 1}, Permissions.ListOfSpeakers.CAN_MANAGE, ) + + def test_permissions_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + self.permission_test_models, + "meeting.delete_all_speakers_of_all_lists", + {"id": 1}, + ) diff --git a/tests/system/action/meeting/test_import.py b/tests/system/action/meeting/test_import.py index 1084f3dc0..71992c1e6 100644 --- a/tests/system/action/meeting/test_import.py +++ b/tests/system/action/meeting/test_import.py @@ -1487,6 +1487,13 @@ def test_missing_required_field(self) -> None: in response.json["message"] ) + def test_locked_meeting(self) -> None: + request_data = self.create_request_data({}) + request_data["meeting"]["meeting"]["1"]["locked_from_inside"] = True + response = self.request("meeting.import", request_data) + self.assert_status_code(response, 400) + assert "Cannot import a locked meeting." in response.json["message"] + def test_field_check(self) -> None: request_data = self.create_request_data( { diff --git a/tests/system/action/meeting/test_set_font.py b/tests/system/action/meeting/test_set_font.py index 7dce18575..a2aa7bda1 100644 --- a/tests/system/action/meeting/test_set_font.py +++ b/tests/system/action/meeting/test_set_font.py @@ -113,3 +113,10 @@ def test_set_font_permission(self) -> None: {"id": 1, "mediafile_id": 17, "place": "bold"}, Permissions.Meeting.CAN_MANAGE_LOGOS_AND_FONTS, ) + + def test_set_font_permission_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + self.permission_test_models, + "meeting.set_font", + {"id": 1, "mediafile_id": 17, "place": "bold"}, + ) diff --git a/tests/system/action/meeting/test_set_logo.py b/tests/system/action/meeting/test_set_logo.py index bcb31b3b3..2833541f1 100644 --- a/tests/system/action/meeting/test_set_logo.py +++ b/tests/system/action/meeting/test_set_logo.py @@ -133,3 +133,10 @@ def test_set_logo_permissions(self) -> None: {"id": 1, "mediafile_id": 17, "place": "web_header"}, Permissions.Meeting.CAN_MANAGE_LOGOS_AND_FONTS, ) + + def test_set_logo_permission_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + self.permission_test_models, + "meeting.set_logo", + {"id": 1, "mediafile_id": 17, "place": "web_header"}, + ) diff --git a/tests/system/action/meeting/test_unarchive.py b/tests/system/action/meeting/test_unarchive.py index 42054934a..f72c22208 100644 --- a/tests/system/action/meeting/test_unarchive.py +++ b/tests/system/action/meeting/test_unarchive.py @@ -60,6 +60,11 @@ def test_unarchive_no_permission(self) -> None: response.json["message"], ) + def test_unarchive_locked_meeting(self) -> None: + self.set_models({"meeting/1": {"locked_from_inside": True}}) + response = self.request("meeting.unarchive", {"id": 1}) + self.assert_status_code(response, 200) + def test_unarchive_meeting_is_not_archived(self) -> None: self.update_model("meeting/1", {"is_active_in_organization_id": 1}) response = self.request("meeting.unarchive", {"id": 1}) diff --git a/tests/system/action/meeting/test_unset_font.py b/tests/system/action/meeting/test_unset_font.py index dd168d0f7..283c8cfb7 100644 --- a/tests/system/action/meeting/test_unset_font.py +++ b/tests/system/action/meeting/test_unset_font.py @@ -63,3 +63,10 @@ def test_unset_font_permissions(self) -> None: {"id": 1, "place": "bold"}, Permissions.Meeting.CAN_MANAGE_LOGOS_AND_FONTS, ) + + def test_unset_font_permissions_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + self.permission_test_models, + "meeting.unset_font", + {"id": 1, "place": "bold"}, + ) diff --git a/tests/system/action/meeting/test_unset_logo.py b/tests/system/action/meeting/test_unset_logo.py index 4a09cc39b..81dc7251b 100644 --- a/tests/system/action/meeting/test_unset_logo.py +++ b/tests/system/action/meeting/test_unset_logo.py @@ -91,3 +91,10 @@ def test_unset_logo_permissions(self) -> None: {"id": 1, "place": "web_header"}, Permissions.Meeting.CAN_MANAGE_LOGOS_AND_FONTS, ) + + def test_unset_logo_permissions_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + self.permission_test_models, + "meeting.unset_logo", + {"id": 1, "place": "web_header"}, + ) diff --git a/tests/system/action/meeting/test_update.py b/tests/system/action/meeting/test_update.py index 7b7e03386..00cdab79c 100644 --- a/tests/system/action/meeting/test_update.py +++ b/tests/system/action/meeting/test_update.py @@ -378,7 +378,11 @@ def test_update_group_a_permissions(self) -> None: self.base_permission_test( self.test_models, "meeting.update", - {"id": 1, "welcome_title": "Hallo"}, + { + "id": 1, + "welcome_title": "Hallo", + "locked_from_inside": True, + }, Permissions.Meeting.CAN_MANAGE_SETTINGS, ) @@ -499,6 +503,97 @@ def test_update_group_f_permissions(self) -> None: OrganizationManagementLevel.SUPERADMIN, ) + def test_update_with_locked_meeting_group_a(self) -> None: + self.base_permission_test( + self.test_models, + "meeting.update", + { + "id": 1, + "welcome_title": "Hallo", + "locked_from_inside": True, + }, + OrganizationManagementLevel.SUPERADMIN, + True, + lock_meeting=True, + ) + + def test_update_with_locked_meeting_group_b(self) -> None: + self.base_permission_test( + self.test_models, + "meeting.update", + {"id": 1, "present_user_ids": [2]}, + OrganizationManagementLevel.SUPERADMIN, + True, + lock_meeting=True, + ) + + def test_update_with_locked_meeting_group_c(self) -> None: + self.base_permission_test( + self.test_models, + "meeting.update", + {"id": 1, "reference_projector_id": 1}, + OrganizationManagementLevel.SUPERADMIN, + True, + lock_meeting=True, + ) + + def test_update_with_locked_meeting_group_c_allowed(self) -> None: + self.base_permission_test( + self.test_models, + "meeting.update", + {"id": 1, "reference_projector_id": 1}, + Permissions.Projector.CAN_MANAGE, + lock_meeting=True, + ) + + def test_update_with_locked_meeting_group_d(self) -> None: + self.create_meeting() + self.user_id = self.create_user("user") + self.login(self.user_id) + self.set_models(self.test_models) + self.set_models({"meeting/1": {"locked_from_inside": True}}) + self.set_user_groups(self.user_id, [3]) + self.set_organization_management_level( + OrganizationManagementLevel.SUPERADMIN, self.user_id + ) + response = self.request( + "meeting.update", + { + "id": 1, + "custom_translations": {"motion": "Antrag", "assignment": "Zuordnung"}, + "external_id": "test", + }, + ) + self.assert_status_code(response, 403) + self.assertIn( + "Missing permission: Not admin of this meeting", + response.json["message"], + ) + + def test_update_with_locked_meeting_group_e(self) -> None: + self.set_models(self.test_models) + self.base_permission_test( + {"organization_tag/1": {}}, + "meeting.update", + {"id": 1, "organization_tag_ids": [1]}, + OrganizationManagementLevel.SUPERADMIN, + lock_meeting=True, + ) + + def test_update_with_locked_meeting_group_f(self) -> None: + self.base_permission_test( + self.test_models, + "meeting.update", + { + "id": 1, + "jitsi_domain": "test", + "jitsi_room_name": "room1", + "jitsi_room_password": "blablabla", + }, + OrganizationManagementLevel.SUPERADMIN, + lock_meeting=True, + ) + def test_update_list_of_speakers_enable_point_of_order_speakers(self) -> None: self.basic_test({"list_of_speakers_enable_point_of_order_speakers": True}) self.assert_model_exists( @@ -647,3 +742,84 @@ def test_update_external_id_self(self) -> None: ) response = self.request("meeting.update", {"id": 1, "external_id": external_id}) self.assert_status_code(response, 200) + + def test_update_cant_lock_template(self) -> None: + self.set_models(self.test_models) + response = self.request( + "meeting.update", + { + "id": 1, + "set_as_template": True, + "locked_from_inside": True, + "location": "Geneva", + }, + ) + self.assert_status_code(response, 400) + self.assertIn( + "A meeting cannot be locked from the inside and a template at the same time.", + response.json["message"], + ) + + def test_update_cant_lock_template_2(self) -> None: + self.create_meeting() + self.set_models(self.test_models) + self.set_models( + { + "meeting/1": { + "template_for_organization_id": 1, + "locked_from_inside": True, + "admin_group_id": 2, + } + } + ) + self.set_user_groups(1, [2]) + response = self.request( + "meeting.update", + {"id": 1, "location": "Geneva"}, + ) + self.assert_status_code(response, 400) + self.assertIn( + "A meeting cannot be locked from the inside and a template at the same time.", + response.json["message"], + ) + + def test_update_cant_lock_template_3(self) -> None: + self.set_models(self.test_models) + self.set_models( + { + "meeting/1": { + "template_for_organization_id": 1, + } + } + ) + response = self.request( + "meeting.update", + {"id": 1, "locked_from_inside": True, "location": "Geneva"}, + ) + self.assert_status_code(response, 400) + self.assertIn( + "A meeting cannot be locked from the inside and a template at the same time.", + response.json["message"], + ) + + def test_update_cant_lock_template_4(self) -> None: + self.create_meeting() + self.set_models(self.test_models) + self.set_models( + { + "meeting/1": { + "locked_from_inside": True, + "admin_group_id": 2, + } + } + ) + self.set_user_groups(1, [2]) + response = self.request( + "meeting.update", + {"id": 1, "set_as_template": True, "location": "Geneva"}, + ) + self.assert_status_code(response, 400) + self.assertIn( + "A meeting cannot be locked from the inside and a template at the same time.", + response.json["message"], + ) diff --git a/tests/system/action/motion/test_create.py b/tests/system/action/motion/test_create.py index 0a4d149e5..c81268708 100644 --- a/tests/system/action/motion/test_create.py +++ b/tests/system/action/motion/test_create.py @@ -413,6 +413,17 @@ def test_create_permission_simple_fields(self) -> None: Permissions.Motion.CAN_CREATE, ) + def test_create_permission_simple_fields_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + {}, + "motion.create", + { + "title": "test_Xcdfgee", + "meeting_id": 1, + "text": "test", + }, + ) + def setup_permission_test( self, permissions: list[Permission], additional_data: dict[str, Any] = {} ) -> None: diff --git a/tests/system/action/motion/test_delete.py b/tests/system/action/motion/test_delete.py index 1e5b6811e..24a92b488 100644 --- a/tests/system/action/motion/test_delete.py +++ b/tests/system/action/motion/test_delete.py @@ -285,6 +285,13 @@ def test_delete_permission(self) -> None: Permissions.Motion.CAN_MANAGE, ) + def test_delete_permission_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + self.permission_test_models, + "motion.delete", + {"id": 112}, + ) + def test_delete_permission_submitter(self) -> None: self.create_meeting() self.user_id = self.create_user("user") diff --git a/tests/system/action/motion/test_follow_recommendation.py b/tests/system/action/motion/test_follow_recommendation.py index fe82b8440..be62b7dec 100644 --- a/tests/system/action/motion/test_follow_recommendation.py +++ b/tests/system/action/motion/test_follow_recommendation.py @@ -272,3 +272,10 @@ def test_follow_recommendation_permission(self) -> None: {"id": 22}, Permissions.Motion.CAN_MANAGE_METADATA, ) + + def test_follow_recommendation_permission_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + self.permission_test_models, + "motion.follow_recommendation", + {"id": 22}, + ) diff --git a/tests/system/action/motion/test_reset_recommendation.py b/tests/system/action/motion/test_reset_recommendation.py index c71aa07c2..3583ba50a 100644 --- a/tests/system/action/motion/test_reset_recommendation.py +++ b/tests/system/action/motion/test_reset_recommendation.py @@ -106,3 +106,10 @@ def test_reset_recommendation_permission(self) -> None: {"id": 22}, Permissions.Motion.CAN_MANAGE_METADATA, ) + + def test_reset_recommendation_permission_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + self.permission_test_models, + "motion.reset_recommendation", + {"id": 22}, + ) diff --git a/tests/system/action/motion/test_reset_state.py b/tests/system/action/motion/test_reset_state.py index a42633d02..b88a1fc34 100644 --- a/tests/system/action/motion/test_reset_state.py +++ b/tests/system/action/motion/test_reset_state.py @@ -173,3 +173,10 @@ def test_reset_state_permission(self) -> None: {"id": 22}, Permissions.Motion.CAN_MANAGE_METADATA, ) + + def test_reset_state_permission_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + self.permission_test_models, + "motion.reset_state", + {"id": 22}, + ) diff --git a/tests/system/action/motion/test_set_recommendation.py b/tests/system/action/motion/test_set_recommendation.py index ce8590d0a..07e637c79 100644 --- a/tests/system/action/motion/test_set_recommendation.py +++ b/tests/system/action/motion/test_set_recommendation.py @@ -191,3 +191,10 @@ def test_set_recommendation_permission(self) -> None: {"id": 22, "recommendation_id": 77}, Permissions.Motion.CAN_MANAGE_METADATA, ) + + def test_set_recommendation_permission_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + self.permission_test_models, + "motion.set_recommendation", + {"id": 22, "recommendation_id": 77}, + ) diff --git a/tests/system/action/motion/test_set_state.py b/tests/system/action/motion/test_set_state.py index ddbc925ea..9c67bc133 100644 --- a/tests/system/action/motion/test_set_state.py +++ b/tests/system/action/motion/test_set_state.py @@ -230,6 +230,13 @@ def test_set_state_permission(self) -> None: Permissions.Motion.CAN_MANAGE_METADATA, ) + def test_set_state_permission_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + {}, + "motion.set_state", + {"id": 22, "state_id": 76}, + ) + def test_set_state_permission_submitter(self) -> None: self.set_models( { diff --git a/tests/system/action/motion/test_set_support_self.py b/tests/system/action/motion/test_set_support_self.py index 389855ded..4e70b53d4 100644 --- a/tests/system/action/motion/test_set_support_self.py +++ b/tests/system/action/motion/test_set_support_self.py @@ -205,6 +205,13 @@ def test_set_support_self_permission(self) -> None: Permissions.Motion.CAN_SUPPORT, ) + def test_set_support_self_permission_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + self.permission_test_models, + "motion.set_support_self", + {"motion_id": 1, "support": True}, + ) + def create_delegator_test_data( self, is_delegator: bool = False, diff --git a/tests/system/action/motion/test_sort.py b/tests/system/action/motion/test_sort.py index 50b6844d8..2387cd257 100644 --- a/tests/system/action/motion/test_sort.py +++ b/tests/system/action/motion/test_sort.py @@ -207,3 +207,10 @@ def test_sort_permission(self) -> None: {"meeting_id": 1, "tree": [{"id": 22}]}, Permissions.Motion.CAN_MANAGE, ) + + def test_sort_permission_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + self.permission_test_models, + "motion.sort", + {"meeting_id": 1, "tree": [{"id": 22}]}, + ) diff --git a/tests/system/action/motion/test_update.py b/tests/system/action/motion/test_update.py index dc9ce44fc..5a23bf440 100644 --- a/tests/system/action/motion/test_update.py +++ b/tests/system/action/motion/test_update.py @@ -96,7 +96,7 @@ def test_update_correct(self) -> None: "motion/111", ["Workflow_timestamp set to {}", "1234567890", "Motion updated"], ) - assert counter.calls == 3 + assert counter.calls == 4 def test_update_wrong_id(self) -> None: self.set_models( @@ -270,7 +270,7 @@ def test_update_correct_2(self) -> None: "Motion updated", ], ) - assert counter.calls == 14 + assert counter.calls == 15 def test_update_workflow_id(self) -> None: self.set_models( diff --git a/tests/system/action/motion_block/test_create.py b/tests/system/action/motion_block/test_create.py index 6a2d6f643..db6d55454 100644 --- a/tests/system/action/motion_block/test_create.py +++ b/tests/system/action/motion_block/test_create.py @@ -82,3 +82,16 @@ def test_create_permissions(self) -> None: {"title": "test_Xcdfgee", "meeting_id": 1}, Permissions.Motion.CAN_MANAGE, ) + + def test_create_permissions_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + { + "meeting/1": { + "name": "test", + "agenda_item_creation": "always", + "is_active_in_organization_id": 1, + } + }, + "motion_block.create", + {"title": "test_Xcdfgee", "meeting_id": 1}, + ) diff --git a/tests/system/action/motion_block/test_delete.py b/tests/system/action/motion_block/test_delete.py index 95d779035..eec9b1d9d 100644 --- a/tests/system/action/motion_block/test_delete.py +++ b/tests/system/action/motion_block/test_delete.py @@ -81,3 +81,10 @@ def test_delete_permissions(self) -> None: {"id": 111}, Permissions.Motion.CAN_MANAGE, ) + + def test_delete_permissions_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + {"motion_block/111": {"meeting_id": 1}}, + "motion_block.delete", + {"id": 111}, + ) diff --git a/tests/system/action/motion_block/test_update.py b/tests/system/action/motion_block/test_update.py index 94624bb45..6301b21bd 100644 --- a/tests/system/action/motion_block/test_update.py +++ b/tests/system/action/motion_block/test_update.py @@ -50,3 +50,12 @@ def test_update_permissions(self) -> None: {"id": 111, "title": "title_Xcdfgee"}, Permissions.Motion.CAN_MANAGE, ) + + def test_update_permissions_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + { + "motion_block/111": {"meeting_id": 1, "title": "title_srtgb123"}, + }, + "motion_block.update", + {"id": 111, "title": "title_Xcdfgee"}, + ) diff --git a/tests/system/action/motion_category/test_create.py b/tests/system/action/motion_category/test_create.py index cc64398a1..20faf178c 100644 --- a/tests/system/action/motion_category/test_create.py +++ b/tests/system/action/motion_category/test_create.py @@ -153,3 +153,13 @@ def test_create_permissions(self) -> None: }, Permissions.Motion.CAN_MANAGE, ) + + def test_create_permissions_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + {}, + "motion_category.create", + { + "name": "test_Xcdfgee", + "meeting_id": 1, + }, + ) diff --git a/tests/system/action/motion_category/test_delete.py b/tests/system/action/motion_category/test_delete.py index 625968ce0..7d8a7de2e 100644 --- a/tests/system/action/motion_category/test_delete.py +++ b/tests/system/action/motion_category/test_delete.py @@ -64,3 +64,10 @@ def test_delete_permissions(self) -> None: {"id": 111}, Permissions.Motion.CAN_MANAGE, ) + + def test_delete_permissions_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + self.permission_test_models, + "motion_category.delete", + {"id": 111}, + ) diff --git a/tests/system/action/motion_category/test_number_motions.py b/tests/system/action/motion_category/test_number_motions.py index c13dbbca7..b1a44bc93 100644 --- a/tests/system/action/motion_category/test_number_motions.py +++ b/tests/system/action/motion_category/test_number_motions.py @@ -469,3 +469,10 @@ def test_number_motions_permissions(self) -> None: {"id": 111}, Permissions.Motion.CAN_MANAGE, ) + + def test_number_motions_permissions_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + self.permission_test_models, + "motion_category.number_motions", + {"id": 111}, + ) diff --git a/tests/system/action/motion_category/test_sort.py b/tests/system/action/motion_category/test_sort.py index 0b0641853..bce98e483 100644 --- a/tests/system/action/motion_category/test_sort.py +++ b/tests/system/action/motion_category/test_sort.py @@ -214,3 +214,10 @@ def test_sort_permission(self) -> None: {"meeting_id": 1, "tree": [{"id": 22}]}, Permissions.Motion.CAN_MANAGE, ) + + def test_sort_permission_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + self.permission_test_models, + "motion_category.sort", + {"meeting_id": 1, "tree": [{"id": 22}]}, + ) diff --git a/tests/system/action/motion_category/test_sort_motions_in_categories.py b/tests/system/action/motion_category/test_sort_motions_in_categories.py index 3597e93d3..c86a6b7d7 100644 --- a/tests/system/action/motion_category/test_sort_motions_in_categories.py +++ b/tests/system/action/motion_category/test_sort_motions_in_categories.py @@ -89,3 +89,10 @@ def test_sort_permission(self) -> None: {"id": 222, "motion_ids": [32, 31]}, Permissions.Motion.CAN_MANAGE, ) + + def test_sort_permission_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + self.permission_test_models, + "motion_category.sort_motions_in_category", + {"id": 222, "motion_ids": [32, 31]}, + ) diff --git a/tests/system/action/motion_category/test_update.py b/tests/system/action/motion_category/test_update.py index 8f1cda2a2..715db6794 100644 --- a/tests/system/action/motion_category/test_update.py +++ b/tests/system/action/motion_category/test_update.py @@ -144,3 +144,13 @@ def test_update_permission(self) -> None: }, Permissions.Motion.CAN_MANAGE, ) + + def test_update_permission_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + self.permission_test_models, + "motion_category.update", + { + "id": 111, + "name": "name_Xcdfgee", + }, + ) diff --git a/tests/system/action/motion_change_recommendation/test_create.py b/tests/system/action/motion_change_recommendation/test_create.py index 788c4d943..b481769a8 100644 --- a/tests/system/action/motion_change_recommendation/test_create.py +++ b/tests/system/action/motion_change_recommendation/test_create.py @@ -113,6 +113,18 @@ def test_create_permission(self) -> None: Permissions.Motion.CAN_MANAGE, ) + def test_create_permission_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + {}, + "motion_change_recommendation.create", + { + "line_from": 125, + "line_to": 234, + "text": "text_DvLXGcdW", + "motion_id": 233, + }, + ) + class MotionChangeRecommendationLineValidationTest(BaseActionTestCase): def setUp(self) -> None: diff --git a/tests/system/action/motion_change_recommendation/test_delete.py b/tests/system/action/motion_change_recommendation/test_delete.py index 1a359f4db..be9114047 100644 --- a/tests/system/action/motion_change_recommendation/test_delete.py +++ b/tests/system/action/motion_change_recommendation/test_delete.py @@ -1,6 +1,5 @@ from typing import Any -from openslides_backend.permissions.permissions import Permissions from tests.system.action.base import BaseActionTestCase @@ -45,10 +44,9 @@ def test_delete_no_permission(self) -> None: {"id": 111}, ) - def test_delete_permission(self) -> None: - self.base_permission_test( + def test_delete_permission_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( self.permission_test_models, "motion_change_recommendation.delete", {"id": 111}, - Permissions.Motion.CAN_MANAGE, ) diff --git a/tests/system/action/motion_change_recommendation/test_update.py b/tests/system/action/motion_change_recommendation/test_update.py index fa135cd61..8c45c0807 100644 --- a/tests/system/action/motion_change_recommendation/test_update.py +++ b/tests/system/action/motion_change_recommendation/test_update.py @@ -110,3 +110,13 @@ def test_update_permission(self) -> None: }, Permissions.Motion.CAN_MANAGE, ) + + def test_update_permission_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + self.permission_test_models, + "motion_change_recommendation.update", + { + "id": 111, + "text": "text_zzTWoMte", + }, + ) diff --git a/tests/system/action/motion_comment/test_create.py b/tests/system/action/motion_comment/test_create.py index 1ab05bcf6..df36ced1c 100644 --- a/tests/system/action/motion_comment/test_create.py +++ b/tests/system/action/motion_comment/test_create.py @@ -133,6 +133,13 @@ def test_create_permission(self) -> None: Permissions.Motion.CAN_SEE, ) + def test_create_permission_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + self.permission_test_models, + "motion_comment.create", + {"comment": "test_Xcdfgee", "motion_id": 357, "section_id": 78}, + ) + def test_create_no_permission_cause_write_group(self) -> None: self.permission_test_models["motion_comment_section/78"]["write_group_ids"] = [ 2 diff --git a/tests/system/action/motion_comment/test_delete.py b/tests/system/action/motion_comment/test_delete.py index 5e6009f78..ce7b7d06b 100644 --- a/tests/system/action/motion_comment/test_delete.py +++ b/tests/system/action/motion_comment/test_delete.py @@ -70,6 +70,13 @@ def test_delete_permissions(self) -> None: Permissions.Motion.CAN_SEE, ) + def test_delete_permissions_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + self.permission_test_models, + "motion_comment.delete", + {"id": 111}, + ) + def test_update_no_permission_cause_write_group(self) -> None: self.permission_test_models["motion_comment_section/78"]["write_group_ids"] = [ 2 diff --git a/tests/system/action/motion_comment_section/test_create.py b/tests/system/action/motion_comment_section/test_create.py index 9238d54b8..dcdb6a751 100644 --- a/tests/system/action/motion_comment_section/test_create.py +++ b/tests/system/action/motion_comment_section/test_create.py @@ -86,3 +86,10 @@ def test_create_permissions(self) -> None: {"name": "test_Xcdfgee", "meeting_id": 1}, Permissions.Motion.CAN_MANAGE, ) + + def test_create_permissions_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + {}, + "motion_comment_section.create", + {"name": "test_Xcdfgee", "meeting_id": 1}, + ) diff --git a/tests/system/action/motion_comment_section/test_delete.py b/tests/system/action/motion_comment_section/test_delete.py index b43e2ddb6..8cf94d0cf 100644 --- a/tests/system/action/motion_comment_section/test_delete.py +++ b/tests/system/action/motion_comment_section/test_delete.py @@ -73,3 +73,10 @@ def test_delete_permissions(self) -> None: {"id": 111}, Permissions.Motion.CAN_MANAGE, ) + + def test_delete_permissions_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + self.permission_test_models, + "motion_comment_section.delete", + {"id": 111}, + ) diff --git a/tests/system/action/motion_comment_section/test_sort.py b/tests/system/action/motion_comment_section/test_sort.py index 8600abf80..cdffe38b0 100644 --- a/tests/system/action/motion_comment_section/test_sort.py +++ b/tests/system/action/motion_comment_section/test_sort.py @@ -113,3 +113,10 @@ def test_sort_permissions(self) -> None: {"meeting_id": 1, "motion_comment_section_ids": [32, 31]}, Permissions.Motion.CAN_MANAGE, ) + + def test_sort_permissions_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + self.permission_test_models, + "motion_comment_section.sort", + {"meeting_id": 1, "motion_comment_section_ids": [32, 31]}, + ) diff --git a/tests/system/action/motion_comment_section/test_update.py b/tests/system/action/motion_comment_section/test_update.py index cc1ec5069..f7141186f 100644 --- a/tests/system/action/motion_comment_section/test_update.py +++ b/tests/system/action/motion_comment_section/test_update.py @@ -94,3 +94,15 @@ def test_update_permissions(self) -> None: }, Permissions.Motion.CAN_MANAGE, ) + + def test_update_permissions_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + self.permission_test_models, + "motion_comment_section.update", + { + "id": 111, + "name": "name_iuqAPRuD", + "read_group_ids": [23], + "write_group_ids": [23], + }, + ) diff --git a/tests/system/action/motion_state/test_create.py b/tests/system/action/motion_state/test_create.py index c4e81e1b0..74a8bdb25 100644 --- a/tests/system/action/motion_state/test_create.py +++ b/tests/system/action/motion_state/test_create.py @@ -262,3 +262,10 @@ def test_create_permissions(self) -> None: {"name": "test_Xcdfgee", "workflow_id": 42}, Permissions.Motion.CAN_MANAGE, ) + + def test_create_permissions_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + self.permission_test_models, + "motion_state.create", + {"name": "test_Xcdfgee", "workflow_id": 42}, + ) diff --git a/tests/system/action/motion_state/test_delete.py b/tests/system/action/motion_state/test_delete.py index ded0e83e4..f87cff2e2 100644 --- a/tests/system/action/motion_state/test_delete.py +++ b/tests/system/action/motion_state/test_delete.py @@ -76,3 +76,10 @@ def test_delete_permissions(self) -> None: {"id": 111}, Permissions.Motion.CAN_MANAGE, ) + + def test_delete_permissions_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + self.permission_test_models, + "motion_state.delete", + {"id": 111}, + ) diff --git a/tests/system/action/motion_state/test_sort.py b/tests/system/action/motion_state/test_sort.py index bddd1ea40..7cb2c263e 100644 --- a/tests/system/action/motion_state/test_sort.py +++ b/tests/system/action/motion_state/test_sort.py @@ -69,3 +69,10 @@ def test_sort_permissions(self) -> None: {"workflow_id": 1, "motion_state_ids": [3, 2, 1]}, Permissions.Motion.CAN_MANAGE, ) + + def test_sort_permissions_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + self.permission_test_models, + "motion_state.sort", + {"workflow_id": 1, "motion_state_ids": [3, 2, 1]}, + ) diff --git a/tests/system/action/motion_state/test_update.py b/tests/system/action/motion_state/test_update.py index 85e377dda..f9714cebd 100644 --- a/tests/system/action/motion_state/test_update.py +++ b/tests/system/action/motion_state/test_update.py @@ -172,3 +172,10 @@ def test_update_permissions(self) -> None: {"id": 111, "name": "name_Xcdfgee"}, Permissions.Motion.CAN_MANAGE, ) + + def test_update_permissions_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + self.permission_test_models, + "motion_state.update", + {"id": 111, "name": "name_Xcdfgee"}, + ) diff --git a/tests/system/action/motion_workflow/test_create.py b/tests/system/action/motion_workflow/test_create.py index be04b0a6f..2417f7092 100644 --- a/tests/system/action/motion_workflow/test_create.py +++ b/tests/system/action/motion_workflow/test_create.py @@ -87,3 +87,10 @@ def test_create_permissions(self) -> None: {"name": "test_Xcdfgee", "meeting_id": 1}, Permissions.Motion.CAN_MANAGE, ) + + def test_create_permissions_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + {}, + "motion_workflow.create", + {"name": "test_Xcdfgee", "meeting_id": 1}, + ) diff --git a/tests/system/action/motion_workflow/test_delete.py b/tests/system/action/motion_workflow/test_delete.py index bbb863743..cd832ceae 100644 --- a/tests/system/action/motion_workflow/test_delete.py +++ b/tests/system/action/motion_workflow/test_delete.py @@ -181,3 +181,20 @@ def test_delete_permissions(self) -> None: {"id": 111}, Permissions.Motion.CAN_MANAGE, ) + + def test_delete_permissions_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + { + "meeting/1": { + "name": "name_testtest", + "motions_default_workflow_id": 12, + "motions_default_statute_amendment_workflow_id": 13, + "motion_workflow_ids": [111, 2], + "is_active_in_organization_id": 1, + }, + "motion_workflow/111": {"name": "name_srtgb123", "meeting_id": 1}, + "motion_workflow/2": {"meeting_id": 1}, + }, + "motion_workflow.delete", + {"id": 111}, + ) diff --git a/tests/system/action/motion_workflow/test_update.py b/tests/system/action/motion_workflow/test_update.py index 4f2ba012b..6183220d1 100644 --- a/tests/system/action/motion_workflow/test_update.py +++ b/tests/system/action/motion_workflow/test_update.py @@ -50,3 +50,12 @@ def test_update_permissions(self) -> None: {"id": 111, "name": "name_Xcdfgee"}, Permissions.Motion.CAN_MANAGE, ) + + def test_update_permissions_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + { + "motion_workflow/111": {"name": "name_srtgb123", "meeting_id": 1}, + }, + "motion_workflow.update", + {"id": 111, "name": "name_Xcdfgee"}, + ) diff --git a/tests/system/action/option/test_update.py b/tests/system/action/option/test_update.py index 0f427d08a..1958eb9e7 100644 --- a/tests/system/action/option/test_update.py +++ b/tests/system/action/option/test_update.py @@ -195,6 +195,13 @@ def test_update_permissions(self) -> None: Permissions.Poll.CAN_MANAGE, ) + def test_update_permissions_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + self.vote_models, + "option.update", + {"id": 57, "Y": "1.000000", "N": "2.000000", "A": "3.000000"}, + ) + def test_update_together_with_poll(self) -> None: self.set_models( { diff --git a/tests/system/action/point_of_order_category/test_create.py b/tests/system/action/point_of_order_category/test_create.py index 456a879ed..eabb25f35 100644 --- a/tests/system/action/point_of_order_category/test_create.py +++ b/tests/system/action/point_of_order_category/test_create.py @@ -37,3 +37,10 @@ def test_create_permission(self) -> None: {"text": "blablabla", "rank": 11, "meeting_id": 1}, Permissions.Meeting.CAN_MANAGE_SETTINGS, ) + + def test_create_permission_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + {}, + "point_of_order_category.create", + {"text": "blablabla", "rank": 11, "meeting_id": 1}, + ) diff --git a/tests/system/action/point_of_order_category/test_delete.py b/tests/system/action/point_of_order_category/test_delete.py index 52b52d108..79fc9ad2b 100644 --- a/tests/system/action/point_of_order_category/test_delete.py +++ b/tests/system/action/point_of_order_category/test_delete.py @@ -48,3 +48,10 @@ def test_delete_permission(self) -> None: {"id": 53}, Permissions.Meeting.CAN_MANAGE_SETTINGS, ) + + def test_delete_permission_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + self.permission_test_models, + "point_of_order_category.delete", + {"id": 53}, + ) diff --git a/tests/system/action/point_of_order_category/test_update.py b/tests/system/action/point_of_order_category/test_update.py index bf25ec70d..ca70b4100 100644 --- a/tests/system/action/point_of_order_category/test_update.py +++ b/tests/system/action/point_of_order_category/test_update.py @@ -51,3 +51,10 @@ def test_update_permission(self) -> None: {"id": 37, "text": "foo", "rank": 12}, Permissions.Meeting.CAN_MANAGE_SETTINGS, ) + + def test_update_permission_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + self.permission_test_models, + "point_of_order_category.update", + {"id": 37, "text": "foo", "rank": 12}, + ) diff --git a/tests/system/action/poll/test_anonymize.py b/tests/system/action/poll/test_anonymize.py index 3a8d7440f..a54541a10 100644 --- a/tests/system/action/poll/test_anonymize.py +++ b/tests/system/action/poll/test_anonymize.py @@ -113,3 +113,10 @@ def test_anonymize_permissions(self) -> None: {"id": 1}, Permissions.Poll.CAN_MANAGE, ) + + def test_anonymize_permissions_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + {}, + "poll.anonymize", + {"id": 1}, + ) diff --git a/tests/system/action/poll/test_create.py b/tests/system/action/poll/test_create.py index 7456face1..e7a800f27 100644 --- a/tests/system/action/poll/test_create.py +++ b/tests/system/action/poll/test_create.py @@ -841,6 +841,24 @@ def test_create_permissions_assignment(self) -> None: Permissions.Assignment.CAN_MANAGE, ) + def test_create_permissions_assignment_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + {}, + "poll.create", + { + "title": "test", + "type": "analog", + "content_object_id": "assignment/1", + "pollmethod": "Y", + "options": [{"text": "test2", "Y": "10.000000"}], + "meeting_id": 1, + "global_yes": True, + "global_no": True, + "global_abstain": True, + "onehundred_percent_base": "Y", + }, + ) + def test_create_forbidden_to_create_poll(self) -> None: self.set_models( { diff --git a/tests/system/action/poll/test_delete.py b/tests/system/action/poll/test_delete.py index 9628d9b34..639d04c9f 100644 --- a/tests/system/action/poll/test_delete.py +++ b/tests/system/action/poll/test_delete.py @@ -111,6 +111,13 @@ def test_delete_permissions(self) -> None: Permissions.Poll.CAN_MANAGE, ) + def test_delete_permissions_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + {"poll/111": {"meeting_id": 1}}, + "poll.delete", + {"id": 111}, + ) + def test_delete_datastore_calls(self) -> None: self.prepare_users_and_poll(3) @@ -119,7 +126,7 @@ def test_delete_datastore_calls(self) -> None: self.assert_status_code(response, 200) self.assert_model_deleted("poll/1") - assert counter.calls == 6 + assert counter.calls == 7 @performance def test_delete_performance(self) -> None: diff --git a/tests/system/action/poll/test_publish.py b/tests/system/action/poll/test_publish.py index 4dde0b00b..b7d5fad5c 100644 --- a/tests/system/action/poll/test_publish.py +++ b/tests/system/action/poll/test_publish.py @@ -82,3 +82,10 @@ def test_publish_permissions(self) -> None: {"id": 1}, Permissions.Poll.CAN_MANAGE, ) + + def test_publish_permissions_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + self.test_models, + "poll.publish", + {"id": 1}, + ) diff --git a/tests/system/action/poll/test_reset.py b/tests/system/action/poll/test_reset.py index 769c84b5e..28a4166e3 100644 --- a/tests/system/action/poll/test_reset.py +++ b/tests/system/action/poll/test_reset.py @@ -104,6 +104,13 @@ def test_reset_permissions(self) -> None: Permissions.Poll.CAN_MANAGE, ) + def test_reset_permissions_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + self.test_models, + "poll.reset", + {"id": 1}, + ) + def test_reset_not_allowed_to_vote_again(self) -> None: self.set_models(self.test_models) self.set_models( @@ -144,7 +151,7 @@ def test_reset_datastore_calls(self) -> None: self.assert_model_exists( "poll/1", {"voted_ids": [], "state": Poll.STATE_CREATED} ) - assert counter.calls == 4 + assert counter.calls == 5 @performance def test_reset_performance(self) -> None: diff --git a/tests/system/action/poll/test_stop.py b/tests/system/action/poll/test_stop.py index 09f376d4d..7446b6671 100644 --- a/tests/system/action/poll/test_stop.py +++ b/tests/system/action/poll/test_stop.py @@ -434,6 +434,15 @@ def test_stop_permissions(self) -> None: Permissions.Poll.CAN_MANAGE, ) + def test_stop_permissions_locked_meeting(self) -> None: + self.set_models(self.test_models) + self.start_poll(1) + self.base_locked_out_superadmin_permission_test( + {}, + "poll.stop", + {"id": 1}, + ) + def test_stop_datastore_calls(self) -> None: user_ids = self.prepare_users_and_poll(3) @@ -443,8 +452,8 @@ def test_stop_datastore_calls(self) -> None: self.assert_status_code(response, 200) poll = self.get_model("poll/1") assert poll["voted_ids"] == user_ids - # always 11 plus len(user_ids) calls, dependent of user count - assert counter.calls == 11 + len(user_ids) + # always 12 plus len(user_ids) calls, dependent of user count + assert counter.calls == 12 + len(user_ids) @performance def test_stop_performance(self) -> None: diff --git a/tests/system/action/poll/test_update.py b/tests/system/action/poll/test_update.py index fd5bd904d..353f21b51 100644 --- a/tests/system/action/poll/test_update.py +++ b/tests/system/action/poll/test_update.py @@ -403,6 +403,13 @@ def test_update_permissions(self) -> None: Permissions.Assignment.CAN_MANAGE, ) + def test_update_permissions_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + {}, + "poll.update", + {"title": "test_title_Aishohh1ohd0aiSut7gi", "id": 1}, + ) + def test_update_entitled_users_at_stop_error(self) -> None: response = self.request( "poll.update", diff --git a/tests/system/action/projection/test_delete.py b/tests/system/action/projection/test_delete.py index 6243cd458..61c8d5dea 100644 --- a/tests/system/action/projection/test_delete.py +++ b/tests/system/action/projection/test_delete.py @@ -79,3 +79,10 @@ def test_delete_permissions(self) -> None: {"id": 12}, Permissions.Projector.CAN_MANAGE, ) + + def test_delete_permissions_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + {}, + "projection.delete", + {"id": 12}, + ) diff --git a/tests/system/action/projection/test_update_options.py b/tests/system/action/projection/test_update_options.py index b4c0fa34f..b82c6b277 100644 --- a/tests/system/action/projection/test_update_options.py +++ b/tests/system/action/projection/test_update_options.py @@ -45,3 +45,13 @@ def test_permissions(self) -> None: }, Permissions.Projector.CAN_MANAGE, ) + + def test_permissions_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + self.test_models, + "projection.update_options", + { + "id": 33, + "options": {"bla": []}, + }, + ) diff --git a/tests/system/action/projector/test_add_to_preview.py b/tests/system/action/projector/test_add_to_preview.py index 184eef3a8..5fbae5232 100644 --- a/tests/system/action/projector/test_add_to_preview.py +++ b/tests/system/action/projector/test_add_to_preview.py @@ -181,3 +181,15 @@ def test_add_to_preview_permission(self) -> None: }, Permissions.Projector.CAN_MANAGE, ) + + def test_add_to_preview_permission_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + {}, + "projector.add_to_preview", + { + "ids": [1, 2], + "content_object_id": "assignment/1", + "stable": False, + "meeting_id": 1, + }, + ) diff --git a/tests/system/action/projector/test_control_view.py b/tests/system/action/projector/test_control_view.py index 0b31a672c..fdadb8774 100644 --- a/tests/system/action/projector/test_control_view.py +++ b/tests/system/action/projector/test_control_view.py @@ -82,3 +82,10 @@ def test_control_view_permissions(self) -> None: {"id": 1, "field": "scale", "direction": "reset"}, Permissions.Projector.CAN_MANAGE, ) + + def test_control_view_permissions_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + {}, + "projector.control_view", + {"id": 1, "field": "scale", "direction": "reset"}, + ) diff --git a/tests/system/action/projector/test_create.py b/tests/system/action/projector/test_create.py index c4aed82b3..fb87d699b 100644 --- a/tests/system/action/projector/test_create.py +++ b/tests/system/action/projector/test_create.py @@ -138,3 +138,13 @@ def test_create_permissions(self) -> None: }, Permissions.Projector.CAN_MANAGE, ) + + def test_create_permissions_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + {}, + "projector.create", + { + "name": "test projector", + "meeting_id": 1, + }, + ) diff --git a/tests/system/action/projector/test_delete.py b/tests/system/action/projector/test_delete.py index 3d944ef0a..fba99ad92 100644 --- a/tests/system/action/projector/test_delete.py +++ b/tests/system/action/projector/test_delete.py @@ -92,3 +92,10 @@ def test_delete_permissions(self) -> None: {"id": 111}, Permissions.Projector.CAN_MANAGE, ) + + def test_delete_permissions_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + {}, + "projector.delete", + {"id": 111}, + ) diff --git a/tests/system/action/projector/test_next.py b/tests/system/action/projector/test_next.py index fedbd3b20..c871b7641 100644 --- a/tests/system/action/projector/test_next.py +++ b/tests/system/action/projector/test_next.py @@ -93,3 +93,10 @@ def test_next_permissions(self) -> None: {"id": 4}, Permissions.Projector.CAN_MANAGE, ) + + def test_next_permissions_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + {}, + "projector.next", + {"id": 4}, + ) diff --git a/tests/system/action/projector/test_previous.py b/tests/system/action/projector/test_previous.py index 7b34131f9..98bdd33ab 100644 --- a/tests/system/action/projector/test_previous.py +++ b/tests/system/action/projector/test_previous.py @@ -96,3 +96,10 @@ def test_previous_permissions(self) -> None: {"id": 4}, Permissions.Projector.CAN_MANAGE, ) + + def test_previous_permissions_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + {}, + "projector.previous", + {"id": 4}, + ) diff --git a/tests/system/action/projector/test_project.py b/tests/system/action/projector/test_project.py index 4bfd82bf4..cb0a3e1b1 100644 --- a/tests/system/action/projector/test_project.py +++ b/tests/system/action/projector/test_project.py @@ -549,3 +549,17 @@ def test_project_permissions(self) -> None: }, Permissions.Projector.CAN_MANAGE, ) + + def test_project_permissions_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + {}, + "projector.project", + { + "ids": [23], + "content_object_id": "assignment/453", + "meeting_id": 1, + "options": {}, + "stable": False, + "type": "test", + }, + ) diff --git a/tests/system/action/projector/test_project_preview.py b/tests/system/action/projector/test_project_preview.py index f878b7edb..db18cecbf 100644 --- a/tests/system/action/projector/test_project_preview.py +++ b/tests/system/action/projector/test_project_preview.py @@ -94,3 +94,10 @@ def test_project_preview_permissions(self) -> None: {"id": 3}, Permissions.Projector.CAN_MANAGE, ) + + def test_project_preview_permissions_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + {}, + "projector.project_preview", + {"id": 3}, + ) diff --git a/tests/system/action/projector/test_sort_preview.py b/tests/system/action/projector/test_sort_preview.py index f8a71db09..69f4c312a 100644 --- a/tests/system/action/projector/test_sort_preview.py +++ b/tests/system/action/projector/test_sort_preview.py @@ -61,3 +61,10 @@ def test_sort_permissions(self) -> None: {"id": 1, "projection_ids": [2, 3, 1]}, Permissions.Projector.CAN_MANAGE, ) + + def test_sort_permissions_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + {}, + "projector.sort_preview", + {"id": 1, "projection_ids": [2, 3, 1]}, + ) diff --git a/tests/system/action/projector/test_toggle.py b/tests/system/action/projector/test_toggle.py index 4d581b9da..47c740eb1 100644 --- a/tests/system/action/projector/test_toggle.py +++ b/tests/system/action/projector/test_toggle.py @@ -126,3 +126,10 @@ def test_toggle_permission(self) -> None: {"ids": [23], "content_object_id": "poll/788", "meeting_id": 1}, Permissions.Projector.CAN_MANAGE, ) + + def test_toggle_permission_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + self.permission_test_models, + "projector.toggle", + {"ids": [23], "content_object_id": "poll/788", "meeting_id": 1}, + ) diff --git a/tests/system/action/projector/test_update.py b/tests/system/action/projector/test_update.py index be954e976..6b51ce471 100644 --- a/tests/system/action/projector/test_update.py +++ b/tests/system/action/projector/test_update.py @@ -250,3 +250,14 @@ def test_update_permissions(self) -> None: }, Permissions.Projector.CAN_MANAGE, ) + + def test_update_permissions_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + self.permission_test_models, + "projector.update", + { + "id": 111, + "name": "name_Xcdfgee", + "width": 100, + }, + ) diff --git a/tests/system/action/projector_countdown/test_create.py b/tests/system/action/projector_countdown/test_create.py index ac26f5738..c8d368375 100644 --- a/tests/system/action/projector_countdown/test_create.py +++ b/tests/system/action/projector_countdown/test_create.py @@ -101,3 +101,15 @@ def test_create_permissions(self) -> None: }, Permissions.Projector.CAN_MANAGE, ) + + def test_create_permissions_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + {}, + "projector_countdown.create", + { + "meeting_id": 1, + "title": "test", + "description": "good description", + "default_time": 30, + }, + ) diff --git a/tests/system/action/projector_countdown/test_delete.py b/tests/system/action/projector_countdown/test_delete.py index 762bc30bf..3cc2c6e7c 100644 --- a/tests/system/action/projector_countdown/test_delete.py +++ b/tests/system/action/projector_countdown/test_delete.py @@ -91,3 +91,10 @@ def test_delete_permissions(self) -> None: {"id": 1}, Permissions.Projector.CAN_MANAGE, ) + + def test_delete_permissions_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + {}, + "projector_countdown.delete", + {"id": 1}, + ) diff --git a/tests/system/action/projector_countdown/test_update.py b/tests/system/action/projector_countdown/test_update.py index 80baa53f8..c07b730e0 100644 --- a/tests/system/action/projector_countdown/test_update.py +++ b/tests/system/action/projector_countdown/test_update.py @@ -77,3 +77,10 @@ def test_update_permissions(self) -> None: {"id": 2, "title": "new_title"}, Permissions.Projector.CAN_MANAGE, ) + + def test_update_permissions_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + {}, + "projector_countdown.update", + {"id": 2, "title": "new_title"}, + ) diff --git a/tests/system/action/projector_message/test_create.py b/tests/system/action/projector_message/test_create.py index 926f48d0e..20b694406 100644 --- a/tests/system/action/projector_message/test_create.py +++ b/tests/system/action/projector_message/test_create.py @@ -50,3 +50,13 @@ def test_create_permissions(self) -> None: }, Permissions.Projector.CAN_MANAGE, ) + + def test_create_permissions_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + {}, + "projector_message.create", + { + "meeting_id": 1, + "message": "TEST", + }, + ) diff --git a/tests/system/action/projector_message/test_delete.py b/tests/system/action/projector_message/test_delete.py index 0fcce5765..873a5f224 100644 --- a/tests/system/action/projector_message/test_delete.py +++ b/tests/system/action/projector_message/test_delete.py @@ -61,3 +61,10 @@ def test_delete_permissions(self) -> None: {"id": 2}, Permissions.Projector.CAN_MANAGE, ) + + def test_delete_permissions_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + self.permission_test_models, + "projector_message.delete", + {"id": 2}, + ) diff --git a/tests/system/action/projector_message/test_update.py b/tests/system/action/projector_message/test_update.py index dcba16271..2c118a418 100644 --- a/tests/system/action/projector_message/test_update.py +++ b/tests/system/action/projector_message/test_update.py @@ -52,3 +52,10 @@ def test_update_permissions(self) -> None: {"id": 2, "message": "geredegerede"}, Permissions.Projector.CAN_MANAGE, ) + + def test_update_permissions_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + self.permission_test_models, + "projector_message.update", + {"id": 2, "message": "geredegerede"}, + ) diff --git a/tests/system/action/speaker/test_create.py b/tests/system/action/speaker/test_create.py index 8a75fefad..93000e44d 100644 --- a/tests/system/action/speaker/test_create.py +++ b/tests/system/action/speaker/test_create.py @@ -506,6 +506,13 @@ def test_create_permissions(self) -> None: Permissions.ListOfSpeakers.CAN_MANAGE, ) + def test_create_permissions_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + self.test_models, + "speaker.create", + {"meeting_user_id": 17, "list_of_speakers_id": 23}, + ) + def test_create_permissions_selfadd(self) -> None: self.create_meeting() self.user_id = 7 diff --git a/tests/system/action/speaker/test_delete.py b/tests/system/action/speaker/test_delete.py index 30e03818e..e588f84da 100644 --- a/tests/system/action/speaker/test_delete.py +++ b/tests/system/action/speaker/test_delete.py @@ -105,6 +105,13 @@ def test_delete_permissions(self) -> None: Permissions.ListOfSpeakers.CAN_MANAGE, ) + def test_delete_permissions_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + self.permission_test_models, + "speaker.delete", + {"id": 890}, + ) + def test_delete_self(self) -> None: self.create_meeting() self.user_id = 7 diff --git a/tests/system/action/speaker/test_end_speech.py b/tests/system/action/speaker/test_end_speech.py index 4883bcf8a..d7955bb26 100644 --- a/tests/system/action/speaker/test_end_speech.py +++ b/tests/system/action/speaker/test_end_speech.py @@ -366,3 +366,10 @@ def test_end_speech_permissions(self) -> None: {"id": 890}, Permissions.ListOfSpeakers.CAN_MANAGE, ) + + def test_end_speech_permissions_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + self.models, + "speaker.end_speech", + {"id": 890}, + ) diff --git a/tests/system/action/speaker/test_pause.py b/tests/system/action/speaker/test_pause.py index 70c258bae..6121d29b4 100644 --- a/tests/system/action/speaker/test_pause.py +++ b/tests/system/action/speaker/test_pause.py @@ -190,3 +190,10 @@ def test_pause_permissions(self) -> None: {"id": 890}, Permissions.ListOfSpeakers.CAN_MANAGE, ) + + def test_pause_permissions_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + self.models, + "speaker.pause", + {"id": 890}, + ) diff --git a/tests/system/action/speaker/test_sort.py b/tests/system/action/speaker/test_sort.py index f5f1c43d8..7396e14ee 100644 --- a/tests/system/action/speaker/test_sort.py +++ b/tests/system/action/speaker/test_sort.py @@ -81,3 +81,10 @@ def test_sort_permisions(self) -> None: {"list_of_speakers_id": 222, "speaker_ids": [32, 31]}, Permissions.ListOfSpeakers.CAN_MANAGE, ) + + def test_sort_permissions_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + self.permission_test_models, + "speaker.sort", + {"list_of_speakers_id": 222, "speaker_ids": [32, 31]}, + ) diff --git a/tests/system/action/speaker/test_speak.py b/tests/system/action/speaker/test_speak.py index 114a5d02f..10bed39c6 100644 --- a/tests/system/action/speaker/test_speak.py +++ b/tests/system/action/speaker/test_speak.py @@ -364,6 +364,13 @@ def test_speak_permissions(self) -> None: Permissions.ListOfSpeakers.CAN_MANAGE, ) + def test_speak_permissions_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + self.models, + "speaker.speak", + {"id": 890}, + ) + def test_speak_stop_paused_speaker(self) -> None: self.set_models( { diff --git a/tests/system/action/speaker/test_unpause.py b/tests/system/action/speaker/test_unpause.py index 863fec1f5..170fce0c5 100644 --- a/tests/system/action/speaker/test_unpause.py +++ b/tests/system/action/speaker/test_unpause.py @@ -179,3 +179,10 @@ def test_unpause_permissions(self) -> None: {"id": 890}, Permissions.ListOfSpeakers.CAN_MANAGE, ) + + def test_unpause_permissions_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + self.models, + "speaker.unpause", + {"id": 890}, + ) diff --git a/tests/system/action/speaker/test_update.py b/tests/system/action/speaker/test_update.py index 412abc2ac..47865ccf1 100644 --- a/tests/system/action/speaker/test_update.py +++ b/tests/system/action/speaker/test_update.py @@ -163,6 +163,13 @@ def test_update_permissions(self) -> None: Permissions.ListOfSpeakers.CAN_MANAGE, ) + def test_update_permissions_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + self.models, + "speaker.update", + {"id": 890, "speech_state": SpeechState.PRO}, + ) + def test_update_check_request_user_is_user_not_can_see(self) -> None: self.create_meeting() self.set_models( diff --git a/tests/system/action/structure_level/test_create.py b/tests/system/action/structure_level/test_create.py index c3ba117db..1a6622bb2 100644 --- a/tests/system/action/structure_level/test_create.py +++ b/tests/system/action/structure_level/test_create.py @@ -136,3 +136,13 @@ def test_create_permissions(self) -> None: }, Permissions.User.CAN_MANAGE, ) + + def test_create_permissions_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + {}, + "structure_level.create", + { + "name": "test", + "meeting_id": 1, + }, + ) diff --git a/tests/system/action/structure_level/test_delete.py b/tests/system/action/structure_level/test_delete.py index 0923b70c5..f41d93ed0 100644 --- a/tests/system/action/structure_level/test_delete.py +++ b/tests/system/action/structure_level/test_delete.py @@ -27,3 +27,14 @@ def test_delete_permissions(self) -> None: Permissions.User.CAN_MANAGE, ) self.assert_model_deleted("structure_level/1") + + def test_delete_permissions_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + { + "structure_level/1": {"meeting_id": 1, "name": "test"}, + }, + "structure_level.delete", + { + "id": 1, + }, + ) diff --git a/tests/system/action/structure_level/test_update.py b/tests/system/action/structure_level/test_update.py index ea4486ce2..9ab7c1d85 100644 --- a/tests/system/action/structure_level/test_update.py +++ b/tests/system/action/structure_level/test_update.py @@ -113,3 +113,15 @@ def test_update_permissions(self) -> None: }, Permissions.User.CAN_MANAGE, ) + + def test_update_permissions_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + { + "structure_level/1": {"meeting_id": 1, "name": "test"}, + }, + "structure_level.update", + { + "id": 1, + "name": "test2", + }, + ) diff --git a/tests/system/action/structure_level_list_of_speakers/test_add_time.py b/tests/system/action/structure_level_list_of_speakers/test_add_time.py index e1827f35e..2836bc885 100644 --- a/tests/system/action/structure_level_list_of_speakers/test_add_time.py +++ b/tests/system/action/structure_level_list_of_speakers/test_add_time.py @@ -164,3 +164,10 @@ def test_permissions(self) -> None: {"id": 1}, Permissions.ListOfSpeakers.CAN_MANAGE, ) + + def test_permissions_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + self.models, + "structure_level_list_of_speakers.add_time", + {"id": 1}, + ) diff --git a/tests/system/action/structure_level_list_of_speakers/test_create.py b/tests/system/action/structure_level_list_of_speakers/test_create.py index e0176da3e..421e5ca67 100644 --- a/tests/system/action/structure_level_list_of_speakers/test_create.py +++ b/tests/system/action/structure_level_list_of_speakers/test_create.py @@ -164,3 +164,13 @@ def test_create_permissions(self) -> None: }, Permissions.ListOfSpeakers.CAN_MANAGE, ) + + def test_create_permissions_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + self.models, + "structure_level_list_of_speakers.create", + { + "structure_level_id": 1, + "list_of_speakers_id": 2, + }, + ) diff --git a/tests/system/action/topic/test_create.py b/tests/system/action/topic/test_create.py index ec18744c8..78c182607 100644 --- a/tests/system/action/topic/test_create.py +++ b/tests/system/action/topic/test_create.py @@ -244,3 +244,10 @@ def test_create_permission(self) -> None: {"meeting_id": 1, "title": "test"}, Permissions.AgendaItem.CAN_MANAGE, ) + + def test_create_permission_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + {}, + "topic.create", + {"meeting_id": 1, "title": "test"}, + ) diff --git a/tests/system/action/topic/test_delete.py b/tests/system/action/topic/test_delete.py index ff2ed6172..06e0fdd1f 100644 --- a/tests/system/action/topic/test_delete.py +++ b/tests/system/action/topic/test_delete.py @@ -142,3 +142,10 @@ def test_delete_permission(self) -> None: {"id": 111}, Permissions.AgendaItem.CAN_MANAGE, ) + + def test_delete_permission_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + self.permission_test_models, + "topic.delete", + {"id": 111}, + ) diff --git a/tests/system/action/topic/test_json_upload.py b/tests/system/action/topic/test_json_upload.py index e2e14ea27..50b90333c 100644 --- a/tests/system/action/topic/test_json_upload.py +++ b/tests/system/action/topic/test_json_upload.py @@ -176,6 +176,13 @@ def test_json_upload_permission(self) -> None: Permissions.AgendaItem.CAN_MANAGE, ) + def test_json_upload_permission_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + {}, + "topic.json_upload", + {"data": [{"title": "test"}], "meeting_id": 1}, + ) + class TopicJsonUploadForUseInImport(BaseActionTestCase): def setUp(self) -> None: diff --git a/tests/system/action/topic/test_update.py b/tests/system/action/topic/test_update.py index e71ca16fb..a6d825830 100644 --- a/tests/system/action/topic/test_update.py +++ b/tests/system/action/topic/test_update.py @@ -59,3 +59,10 @@ def test_update_permission(self) -> None: {"id": 1, "title": "test2", "text": "text"}, Permissions.AgendaItem.CAN_MANAGE, ) + + def test_update_permission_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + self.permission_test_models, + "topic.update", + {"id": 1, "title": "test2", "text": "text"}, + ) diff --git a/tests/system/action/user/test_assign_meetings.py b/tests/system/action/user/test_assign_meetings.py index 1aa9c5516..dc4e5433b 100644 --- a/tests/system/action/user/test_assign_meetings.py +++ b/tests/system/action/user/test_assign_meetings.py @@ -295,3 +295,97 @@ def test_assign_meetings_archived_meetings(self) -> None: "Meeting Archived/1 cannot be changed, because it is archived." in response.json["message"] ) + + def test_assign_meetings_with_locked_meetings(self) -> None: + self.set_models( + { + "group/11": { + "name": "to_find", + "meeting_id": 1, + "meeting_user_ids": [1], + }, + "group/22": { + "name": "nothing", + "meeting_id": 2, + "meeting_user_ids": [2], + }, + "group/31": {"name": "to_find", "meeting_id": 3}, + "group/43": {"name": "standard", "meeting_id": 4}, + "group/51": {"name": "to_find", "meeting_id": 5}, + "group/52": { + "name": "nothing", + "meeting_id": 5, + "meeting_user_ids": [5], + }, + "meeting/1": { + "name": "success(existing)", + "group_ids": [11], + "is_active_in_organization_id": 1, + "committee_id": 2, + "meeting_user_ids": [1], + }, + "meeting/2": { + "name": "nothing", + "group_ids": [22], + "is_active_in_organization_id": 1, + "committee_id": 2, + "meeting_user_ids": [2], + "locked_from_inside": True, + }, + "meeting/3": { + "name": "success(added)", + "group_ids": [31], + "is_active_in_organization_id": 1, + "committee_id": 2, + "locked_from_inside": False, + }, + "meeting/4": { + "name": "standard", + "group_ids": [43], + "is_active_in_organization_id": 1, + "default_group_id": 43, + "committee_id": 2, + "locked_from_inside": True, + }, + "meeting/5": { + "name": "success(added)", + "group_ids": [51, 52], + "is_active_in_organization_id": 1, + "committee_id": 2, + "meeting_user_ids": [5], + }, + "user/1": { + "meeting_user_ids": [1, 2, 5], + "meeting_ids": [1, 2, 5], + }, + "meeting_user/1": { + "meeting_id": 1, + "user_id": 1, + "group_ids": [11], + }, + "meeting_user/2": { + "meeting_id": 2, + "user_id": 1, + "group_ids": [22], + }, + "meeting_user/5": { + "meeting_id": 5, + "user_id": 1, + "group_ids": [52], + }, + "committee/2": {"meeting_ids": [1, 2, 3, 4, 5]}, + } + ) + response = self.request( + "user.assign_meetings", + { + "id": 1, + "meeting_ids": [1, 2, 3, 4, 5], + "group_name": "to_find", + }, + ) + self.assert_status_code(response, 400) + assert ( + response.json["message"] + == "Cannot assign meetings because some selected meetings are locked: 2, 4." + ) diff --git a/tests/system/action/user/test_create.py b/tests/system/action/user/test_create.py index 3fc93713b..39ef5705b 100644 --- a/tests/system/action/user/test_create.py +++ b/tests/system/action/user/test_create.py @@ -807,6 +807,31 @@ def test_create_permission_group_B_user_can_manage_no_permission(self) -> None: response.json["message"], ) + def test_create_permission_group_B_locked_meeting(self) -> None: + """Group B fields needs explicit user.can_manage permission for meeting""" + self.permission_setup() + self.set_organization_management_level( + OrganizationManagementLevel.SUPERADMIN, self.user_id + ) + self.create_meeting(4) + self.set_models({"meeting/4": {"locked_from_inside": True}}) + + response = self.request( + "user.create", + { + "username": "usersname", + "meeting_id": 4, + "group_ids": [4], + "is_present_in_meeting_ids": [4], + "number": "number1", + }, + ) + self.assert_status_code(response, 403) + self.assertIn( + "The user needs Permission user.can_manage for meeting 4", + response.json["message"], + ) + def test_create_permission_group_C_oml_manager(self) -> None: """May create group C group_ids by OML permission""" self.permission_setup() @@ -826,6 +851,29 @@ def test_create_permission_group_C_oml_manager(self) -> None: self.assert_model_exists("user/3", {"meeting_user_ids": [2]}) self.assert_model_exists("meeting_user/2", {"group_ids": [1]}) + def test_create_permission_group_C_locked_meeting(self) -> None: + """May not create group C group_ids by OML permission with a locked meeting""" + self.permission_setup() + self.set_organization_management_level( + OrganizationManagementLevel.SUPERADMIN, self.user_id + ) + self.create_meeting(4) + self.set_models({"meeting/4": {"locked_from_inside": True}}) + + response = self.request( + "user.create", + { + "username": "usersname", + "meeting_id": 4, + "group_ids": [4], + }, + ) + self.assert_status_code(response, 403) + self.assertIn( + "The user needs Permission user.can_manage for meeting 4", + response.json["message"], + ) + def test_create_permission_group_C_committee_manager(self) -> None: """May create group C group_ids by committee permission""" self.permission_setup() @@ -905,6 +953,27 @@ def test_create_permission_group_C_no_permission(self) -> None: response.json["message"], ) + def test_create_permission_group_C_cml_locked_meeting(self) -> None: + """May not create group C group_ids in locked meetings as a committee manager""" + self.permission_setup() + self.create_meeting(4) + self.set_committee_management_level([63], self.user_id) + self.set_models({"meeting/4": {"locked_from_inside": True}}) + + response = self.request( + "user.create", + { + "username": "usersname", + "meeting_id": 4, + "group_ids": [4], + }, + ) + self.assert_status_code(response, 403) + self.assertIn( + "The user needs Permission user.can_manage for meeting 4", + response.json["message"], + ) + def test_create_permission_group_D_permission_with_OML(self) -> None: """May create Group D committee fields with OML level permission for more than one committee""" self.permission_setup() diff --git a/tests/system/action/user/test_delete.py b/tests/system/action/user/test_delete.py index 3c821522b..954802bf5 100644 --- a/tests/system/action/user/test_delete.py +++ b/tests/system/action/user/test_delete.py @@ -21,7 +21,7 @@ def test_delete_wrong_id(self) -> None: model = self.get_model("user/112") assert model.get("username") == "username_srtgb123" - def test_delete_correct_with_groups(self) -> None: + def test_delete_correct_with_groups_and_locked_meeting(self) -> None: self.set_models( { "user/111": { @@ -41,6 +41,7 @@ def test_delete_correct_with_groups(self) -> None: "user_ids": [111], "is_active_in_organization_id": 1, "meeting_user_ids": [1111], + "locked_from_inside": True, }, "committee/1": { "meeting_ids": [456], diff --git a/tests/system/action/user/test_merge_together.py b/tests/system/action/user/test_merge_together.py index 6740de2a1..f578d285d 100644 --- a/tests/system/action/user/test_merge_together.py +++ b/tests/system/action/user/test_merge_together.py @@ -510,7 +510,7 @@ def setup_complex_user_fields(self) -> None: { "committee/1": {"manager_ids": [2, 5]}, "committee/3": {"manager_ids": [5]}, - "meeting/2": {"present_user_ids": [4]}, + "meeting/2": {"present_user_ids": [4], "locked_from_inside": True}, "meeting/3": {"present_user_ids": [3, 4]}, "meeting/4": {"present_user_ids": [5]}, "user/2": { diff --git a/tests/system/action/user/test_participant_import.py b/tests/system/action/user/test_participant_import.py index ba8a5c8bb..812302d7c 100644 --- a/tests/system/action/user/test_participant_import.py +++ b/tests/system/action/user/test_participant_import.py @@ -293,6 +293,11 @@ def test_import_permission_2(self) -> None: True, ) + def test_import_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + {}, "participant.import", {"id": 1, "import": True} + ) + class ParticipantJsonImportWithIncludedJsonUpload(ParticipantJsonUploadForUseInImport): def test_upload_import_invalid_vote_weight_with_remove(self) -> None: diff --git a/tests/system/action/user/test_participant_json_upload.py b/tests/system/action/user/test_participant_json_upload.py index 8f4e1acea..f4c9ed7aa 100644 --- a/tests/system/action/user/test_participant_json_upload.py +++ b/tests/system/action/user/test_participant_json_upload.py @@ -284,6 +284,13 @@ def test_json_upload_permission_2(self) -> None: True, ) + def test_json_upload_locked_meeting(self) -> None: + self.base_locked_out_superadmin_permission_test( + {}, + "participant.json_upload", + {"meeting_id": 1, "data": [{"username": "test"}]}, + ) + def test_json_upload_names_and_email_find_add_meeting_data(self) -> None: self.set_models( { diff --git a/tests/system/action/user/test_send_invitation_email.py b/tests/system/action/user/test_send_invitation_email.py index e9472bd3c..17140d3ae 100644 --- a/tests/system/action/user/test_send_invitation_email.py +++ b/tests/system/action/user/test_send_invitation_email.py @@ -715,30 +715,49 @@ def test_with_meeting_permission(self) -> None: self.assert_with_meeting_permission(Permissions.User.CAN_UPDATE) def assert_with_meeting_permission(self, perm: Permission) -> None: - self.base_permission_test( + self.set_models( { "meeting/1": { - "meeting_user_ids": [2], - }, - "user/2": { - "username": "Testuser 2", - "first_name": "Jim", - "last_name": "Beam", - "default_password": "secret", - "email": "recipient2@example.com", - "meeting_user_ids": [2], - "meeting_ids": [1], - }, - "meeting_user/2": { + "users_email_subject": "Invitation for Openslides '{event_name}'", + "users_email_body": "event name: {event_name}", + } + } + ) + self.set_group_permissions(3, [perm]) + handler = AIOHandler() + with AiosmtpdServerManager(handler): + response = self.request( + "user.send_invitation_email", + { + "id": 2, "meeting_id": 1, - "user_id": 2, - "group_ids": [1], }, - }, - "user.send_invitation_email", + ) + self.assert_status_code(response, 200) + assert response.json["results"][0][0]["sent"] + + def test_with_locked_meeting(self) -> None: + self.set_models( { - "id": 2, - "meeting_id": 1, - }, - perm, + "meeting/1": { + "users_email_subject": "Invitation for Openslides '{event_name}'", + "users_email_body": "event name: {event_name}", + "locked_from_inside": True, + } + } + ) + handler = AIOHandler() + with AiosmtpdServerManager(handler): + response = self.request( + "user.send_invitation_email", + { + "id": 2, + "meeting_id": 1, + }, + ) + self.assert_status_code(response, 200) + assert not response.json["results"][0][0]["sent"] + self.assertIn( + "Missing Permission: user.can_update Mail 1 from 1", + response.json["results"][0][0]["message"], ) diff --git a/tests/system/action/user/test_set_present.py b/tests/system/action/user/test_set_present.py index 689d39615..36aebf53a 100644 --- a/tests/system/action/user/test_set_present.py +++ b/tests/system/action/user/test_set_present.py @@ -226,3 +226,52 @@ def test_set_present_self_permission(self) -> None: "user.set_present", {"id": 1, "meeting_id": 1, "present": True} ) self.assert_status_code(response, 200) + + def test_set_present_locked_meeting(self) -> None: + self.set_models( + { + "meeting/1": { + "users_allow_self_set_present": False, + "committee_id": 1, + "is_active_in_organization_id": 1, + "locked_from_inside": True, + }, + "committee/1": {"user_ids": [1]}, + } + ) + response = self.request( + "user.set_present", {"id": 1, "meeting_id": 1, "present": True} + ) + self.assert_status_code(response, 403) + self.assertIn( + "You are not allowed to set present.", + response.json["message"], + ) + + def test_set_present_cml_locked_meeting( + self, + ) -> None: + self.set_models( + { + "meeting/1": { + "users_allow_self_set_present": False, + "committee_id": 1, + "is_active_in_organization_id": 1, + "locked_from_inside": True, + }, + "committee/1": {"user_ids": [1]}, + "user/1": { + "organization_management_level": None, + "committee_ids": [1], + "committee_management_ids": [1], + }, + } + ) + response = self.request( + "user.set_present", {"id": 1, "meeting_id": 1, "present": True} + ) + self.assert_status_code(response, 403) + self.assertIn( + "You are not allowed to set present.", + response.json["message"], + ) diff --git a/tests/system/action/user/test_toggle_presence_by_number.py b/tests/system/action/user/test_toggle_presence_by_number.py index 65ce707e9..f651e0e59 100644 --- a/tests/system/action/user/test_toggle_presence_by_number.py +++ b/tests/system/action/user/test_toggle_presence_by_number.py @@ -216,3 +216,57 @@ def test_toggle_presence_by_number_meeting_can_update_permission(self) -> None: "user.toggle_presence_by_number", {"meeting_id": 1, "number": "test"} ) self.assert_status_code(response, 200) + + def test_toggle_presence_by_number_locked_meeting(self) -> None: + self.set_models( + { + "meeting/1": { + "committee_id": 1, + "is_active_in_organization_id": 1, + "meeting_user_ids": [34], + "locked_from_inside": True, + }, + "user/1": { + "organization_management_level": OrganizationManagementLevel.CAN_MANAGE_USERS, + "meeting_user_ids": [34], + }, + "committee/1": {}, + "meeting_user/34": {"user_id": 1, "meeting_id": 1, "number": "test"}, + } + ) + response = self.request( + "user.toggle_presence_by_number", {"meeting_id": 1, "number": "test"} + ) + self.assert_status_code(response, 403) + self.assertIn( + "You are not allowed to toggle presence by number.", + response.json["message"], + ) + + def test_toggle_presence_by_number_cml_locked_meeting(self) -> None: + self.set_models( + { + "meeting/1": { + "committee_id": 1, + "is_active_in_organization_id": 1, + "meeting_user_ids": [34], + "locked_from_inside": True, + }, + "committee/1": {"user_ids": [1]}, + "user/1": { + "organization_management_level": None, + "committee_ids": [1], + "committee_management_ids": [1], + "meeting_user_ids": [34], + }, + "meeting_user/34": {"user_id": 1, "meeting_id": 1, "number": "test"}, + } + ) + response = self.request( + "user.toggle_presence_by_number", {"meeting_id": 1, "number": "test"} + ) + self.assert_status_code(response, 403) + self.assertIn( + "You are not allowed to toggle presence by number.", + response.json["message"], + ) diff --git a/tests/system/action/user/test_update.py b/tests/system/action/user/test_update.py index 9593fb88e..b68f6d7e0 100644 --- a/tests/system/action/user/test_update.py +++ b/tests/system/action/user/test_update.py @@ -924,6 +924,42 @@ def test_perm_group_A_no_permission(self) -> None: response.json["message"], ) + def test_perm_group_A_locked_meeting(self) -> None: + """May update group A fields on a user who is in a locked meeting""" + self.permission_setup() + self.create_meeting(base=4) + self.set_organization_management_level( + OrganizationManagementLevel.SUPERADMIN, self.user_id + ) + self.set_user_groups(111, [1, 6]) + self.set_models( + { + "organization/1": { + "genders": ["male", "female", "diverse", "non-binary"] + }, + "meeting/4": {"locked_from_inside": True}, + } + ) + + response = self.request( + "user.update", + { + "id": 111, + "username": "new_username", + "title": "new title", + "first_name": "new first_name", + "last_name": "new last_name", + "is_active": True, + "is_physical_person": True, + "default_password": "new default_password", + "gender": "female", + "email": "info@openslides.com ", # space intentionally, will be stripped + "default_vote_weight": "1.234000", + "can_change_own_password": False, + }, + ) + self.assert_status_code(response, 200) + def test_perm_group_F_default_password_for_superadmin_no_permission(self) -> None: """May not update the default_password for superadmin without having permission oml.SUPERADMIN""" self.permission_setup() @@ -1071,6 +1107,34 @@ def test_perm_group_B_user_can_update_no_permission(self) -> None: response.json["message"], ) + def test_perm_group_B_locked_meeting(self) -> None: + """Group B fields needs explicit user.can_update permission for a locked meeting""" + self.permission_setup() + self.create_meeting(base=4) + self.set_organization_management_level( + OrganizationManagementLevel.SUPERADMIN, self.user_id + ) + self.set_user_groups( + self.user_id, [3, 6] + ) # Empty groups of meeting/1 and meeting/4 + self.set_user_groups(111, [1, 4]) # Default groups of meeting/1 and meeting/4 + self.set_group_permissions(3, [Permissions.User.CAN_UPDATE]) + self.set_models({"meeting/4": {"locked_from_inside": True}}) + + response = self.request( + "user.update", + { + "id": 111, + "meeting_id": 4, + "number": "number1 in 4", + }, + ) + self.assert_status_code(response, 403) + self.assertIn( + "The user needs Permission user.can_update for meeting 4", + response.json["message"], + ) + def test_perm_group_C_oml_manager(self) -> None: """May update group C group_ids by OML permission""" self.permission_setup() @@ -1160,6 +1224,135 @@ def test_perm_group_C_no_permission(self) -> None: response.json["message"], ) + def test_perm_group_C_locked_meeting(self) -> None: + """May not update group C group_ids in locked_meetings""" + self.permission_setup() + self.set_organization_management_level( + OrganizationManagementLevel.SUPERADMIN, self.user_id + ) + self.set_models({"meeting/1": {"locked_from_inside": True}}) + + response = self.request( + "user.update", + { + "id": 111, + "meeting_id": 1, + "group_ids": [1], + }, + ) + self.assert_status_code(response, 403) + self.assertIn( + "The user needs Permission user.can_update for meeting 1", + response.json["message"], + ) + + def test_perm_group_C_locked_meeting_and_meeting_member(self) -> None: + """May not update group C group_ids in a locked meeting without appropriate meeting-internal permission""" + self.permission_setup() + self.set_organization_management_level( + OrganizationManagementLevel.SUPERADMIN, self.user_id + ) + self.set_user_groups(self.user_id, [1]) + self.set_models({"meeting/1": {"locked_from_inside": True}}) + + response = self.request( + "user.update", + { + "id": 111, + "meeting_id": 1, + "group_ids": [1], + }, + ) + self.assert_status_code(response, 403) + self.assertIn( + "The user needs Permission user.can_update for meeting 1", + response.json["message"], + ) + + def test_perm_group_C_locked_meeting_cml(self) -> None: + """Committee manager may not update group C group_ids if the meeting is locked""" + self.permission_setup() + self.set_committee_management_level([60], self.user_id) + self.set_models({"meeting/1": {"locked_from_inside": True}}) + + response = self.request( + "user.update", + { + "id": 111, + "meeting_id": 1, + "group_ids": [1], + }, + ) + self.assert_status_code(response, 403) + self.assertIn( + "The user needs Permission user.can_update for meeting 1", + response.json["message"], + ) + + def test_perm_group_C_locked_meeting_cml_and_meeting_member(self) -> None: + """Meeting manager may not update group C group_ids, if the meeting is locked and he doesn't have the correct meeting-internal permissions""" + self.permission_setup() + self.set_committee_management_level([60], self.user_id) + self.set_user_groups(self.user_id, [1]) + self.set_models({"meeting/1": {"locked_from_inside": True}}) + + response = self.request( + "user.update", + { + "id": 111, + "meeting_id": 1, + "group_ids": [1], + }, + ) + self.assert_status_code(response, 403) + self.assertIn( + "The user needs Permission user.can_update for meeting 1", + response.json["message"], + ) + + def test_perm_group_C_locked_meeting_admin(self) -> None: + """May update group C group_ids in a locked meeting as the meeting admin""" + self.permission_setup() + self.set_user_groups(self.user_id, [2]) + self.set_models({"meeting/1": {"locked_from_inside": True}}) + + response = self.request( + "user.update", + { + "id": 111, + "meeting_id": 1, + "group_ids": [1], + }, + ) + self.assert_status_code(response, 200) + + def test_perm_group_C_locked_meeting_other_meeting(self) -> None: + """ + May update group C group_ids for a non-locked meeting, + even if the user is in another meeting, which is locked + """ + self.permission_setup() + self.create_meeting(base=4) + self.set_organization_management_level( + OrganizationManagementLevel.SUPERADMIN, self.user_id + ) + self.set_user_groups( + self.user_id, [3, 6] + ) # Empty groups of meeting/1 and meeting/4 + self.set_user_groups(111, [1, 4]) # Default groups of meeting/1 and meeting/4 + self.set_group_permissions(3, [Permissions.User.CAN_UPDATE]) + self.set_models({"meeting/4": {"locked_from_inside": True}}) + + response = self.request( + "user.update", + { + "id": 111, + "meeting_id": 1, + "group_ids": [1], + }, + ) + self.assert_status_code(response, 200) + def test_perm_group_C_special_1(self) -> None: """group C group_ids adding meeting in same committee with committee permission""" self.permission_setup() @@ -1323,6 +1516,26 @@ def test_perm_group_D_no_permission(self) -> None: response.json["message"], ) + def test_perm_group_D_locked_meeting(self) -> None: + """May update Group D committee fields if there is a locked meeting""" + self.permission_setup() + self.create_meeting(base=4) + self.set_committee_management_level([60], self.user_id) + self.set_committee_management_level([60], 111) + self.set_organization_management_level( + OrganizationManagementLevel.SUPERADMIN, self.user_id + ) + self.set_models({"meeting/4": {"locked_from_inside": True}}) + + response = self.request( + "user.update", + { + "id": 111, + "committee_management_ids": [63], + }, + ) + self.assert_status_code(response, 200) + def test_perm_group_D_permission_with_CML_and_untouched_committee( self, ) -> None: @@ -1417,6 +1630,23 @@ def test_perm_group_E_OML_not_high_enough(self) -> None: response.json["message"], ) + def test_perm_group_E_locked_meeting(self) -> None: + """May edit OML, even if the user is in a locked meeting""" + self.permission_setup() + self.set_organization_management_level( + OrganizationManagementLevel.SUPERADMIN, self.user_id + ) + self.set_models({"meeting/1": {"locked_from_inside": True}}) + + response = self.request( + "user.update", + { + "id": 111, + "organization_management_level": OrganizationManagementLevel.CAN_MANAGE_ORGANIZATION, + }, + ) + self.assert_status_code(response, 200) + def test_perm_group_F_demo_user_permission(self) -> None: """demo_user only editable by Superadmin""" self.permission_setup() @@ -1440,6 +1670,24 @@ def test_perm_group_F_demo_user_permission(self) -> None: }, ) + def test_perm_group_F_locked_meeting(self) -> None: + """demo_user is editable by Superadmin, even on users in locked meetings""" + self.permission_setup() + self.set_organization_management_level( + OrganizationManagementLevel.SUPERADMIN, self.user_id + ) + self.set_models({"meeting/1": {"locked_from_inside": True}}) + + response = self.request( + "user.update", + { + "id": 111, + "is_demo_user": True, + }, + ) + + self.assert_status_code(response, 200) + def test_no_perm_group_H_internal_saml_id(self) -> None: self.permission_setup() self.set_organization_management_level( diff --git a/tests/system/presenter/test_export_meeting.py b/tests/system/presenter/test_export_meeting.py index 176ca57c0..8dcc51970 100644 --- a/tests/system/presenter/test_export_meeting.py +++ b/tests/system/presenter/test_export_meeting.py @@ -59,6 +59,16 @@ def test_no_permissions(self) -> None: status_code, data = self.request("export_meeting", {"meeting_id": 1}) assert status_code == 403 + def test_with_locked_meeting(self) -> None: + self.set_models( + { + "meeting/1": {"name": "test_foo", "locked_from_inside": True}, + } + ) + status_code, data = self.request("export_meeting", {"meeting_id": 1}) + assert status_code == 400 + assert data["message"] == "Cannot export: meeting 1 is locked." + def test_organization_tags_exclusion(self) -> None: self.set_models( { diff --git a/tests/system/presenter/test_get_forwarding_committees.py b/tests/system/presenter/test_get_forwarding_committees.py index 8ea91c0b6..e7bb4cd45 100644 --- a/tests/system/presenter/test_get_forwarding_committees.py +++ b/tests/system/presenter/test_get_forwarding_committees.py @@ -91,3 +91,14 @@ def test_no_permissions(self) -> None: status_code, data = self.request("get_forwarding_committees", {"meeting_id": 3}) assert status_code == 403 assert "Missing permission" in data["message"] + + def test_with_locked_meeting(self) -> None: + self.set_models( + { + "meeting/3": {"group_ids": [3], "locked_from_inside": True}, + "group/3": {"meeting_id": 3}, + } + ) + status_code, data = self.request("get_forwarding_committees", {"meeting_id": 3}) + assert status_code == 403 + assert "Missing permission: motion.can_manage_metadata" in data["message"] diff --git a/tests/system/presenter/test_get_forwarding_meetings.py b/tests/system/presenter/test_get_forwarding_meetings.py index a9cf5b816..78984caf2 100644 --- a/tests/system/presenter/test_get_forwarding_meetings.py +++ b/tests/system/presenter/test_get_forwarding_meetings.py @@ -301,3 +301,14 @@ def test_sender_meeting_without_committee(self) -> None: "message": "There is no committee given for meeting/1 meeting1.", }, ) + + def test_with_locked_meeting(self) -> None: + self.set_models( + { + "meeting/3": {"group_ids": [3], "locked_from_inside": True}, + "group/3": {"meeting_id": 3}, + } + ) + status_code, data = self.request("get_forwarding_meetings", {"meeting_id": 3}) + assert status_code == 403 + assert "Missing permission: motion.can_manage" in data["message"] diff --git a/tests/system/presenter/test_get_user_related_models.py b/tests/system/presenter/test_get_user_related_models.py index 50fc4c93d..8285a2a66 100644 --- a/tests/system/presenter/test_get_user_related_models.py +++ b/tests/system/presenter/test_get_user_related_models.py @@ -1,3 +1,5 @@ +from typing import Any, cast + from openslides_backend.permissions.management_levels import OrganizationManagementLevel from openslides_backend.permissions.permissions import Permissions @@ -124,6 +126,7 @@ def test_get_user_related_models_meeting(self) -> None: "id": 1, "name": "test", "is_active_in_organization_id": 1, + "is_locked": False, "motion_submitter_ids": [2], "assignment_candidate_ids": [3], "speaker_ids": [4], @@ -181,6 +184,7 @@ def test_get_user_related_models_meetings_more_users(self) -> None: "id": 1, "name": "test", "is_active_in_organization_id": 1, + "is_locked": False, "motion_submitter_ids": [2], "assignment_candidate_ids": [3], "speaker_ids": [4], @@ -193,6 +197,7 @@ def test_get_user_related_models_meetings_more_users(self) -> None: "id": 1, "name": "test", "is_active_in_organization_id": 1, + "is_locked": False, "motion_submitter_ids": [3], "assignment_candidate_ids": [4], "speaker_ids": [5], @@ -251,6 +256,7 @@ def test_get_user_related_models_meetings_more_users_ignore_one_meeting_user( "id": 1, "name": "test", "is_active_in_organization_id": 1, + "is_locked": False, "motion_submitter_ids": [2], "assignment_candidate_ids": [3], "speaker_ids": [4], @@ -313,6 +319,7 @@ def test_get_user_related_models_empty_meeting( "id": 1, "name": "test", "is_active_in_organization_id": 1, + "is_locked": False, } ] }, @@ -371,6 +378,7 @@ def test_get_user_related_models_archived_meeting( "id": 1, "name": "test", "is_active_in_organization_id": None, + "is_locked": False, } ] }, @@ -476,3 +484,75 @@ def test_get_user_related_models_no_permissions_higher_oml(self) -> None: data["message"] == "Missing permission: OrganizationManagementLevel superadmin in organization 1" ) + + def test_get_user_related_models_with_locked_meetings(self) -> None: + self.set_models( + { + "user/1": {"meeting_ids": [2]}, + "user/2": {"meeting_ids": [1, 2, 3], "meeting_user_ids": [1, 2, 3]}, + "committee/1": {"meeting_ids": [1, 2, 3]}, + **{ + key: cast(dict[str, Any], value) + for id_ in [1, 2, 3] + for key, value in { + f"meeting/{id_}": { + "name": f"test{id_}", + "is_active_in_organization_id": 1, + "committee_id": id_, + "group_ids": [id_], + "locked_from_inside": id_ != 1, + }, + f"motion_submitter/{id_}": { + "meeting_user_id": id_, + "meeting_id": id_, + }, + f"assignment_candidate/{id_}": { + "meeting_user_id": id_, + "meeting_id": id_, + }, + f"speaker/{id_}": {"meeting_user_id": id_, "meeting_id": id_}, + f"meeting_user/{id_}": { + "meeting_id": id_, + "user_id": 2, + "speaker_ids": [id_], + "motion_submitter_ids": [id_], + "assignment_candidate_ids": [id_], + "group_ids": [id_], + }, + f"group/{id_}": {"meeting_id": id_, "meeting_user_ids": [id_]}, + }.items() + }, + } + ) + status_code, data = self.request("get_user_related_models", {"user_ids": [2]}) + self.assertEqual(status_code, 200) + assert data == { + "2": { + "meetings": [ + { + "id": 1, + "name": "test1", + "is_active_in_organization_id": 1, + "is_locked": False, + "motion_submitter_ids": [1], + "assignment_candidate_ids": [1], + "speaker_ids": [1], + }, + { + "id": 2, + "name": "test2", + "is_active_in_organization_id": 1, + "is_locked": True, + "motion_submitter_ids": [2], + "assignment_candidate_ids": [2], + "speaker_ids": [2], + }, + { + "id": 3, + "name": "test3", + "is_active_in_organization_id": 1, + "is_locked": True, + }, + ] + }, + } diff --git a/tests/system/presenter/test_search_for_id_by_external_id.py b/tests/system/presenter/test_search_for_id_by_external_id.py index 095ad87cb..6aa04d602 100644 --- a/tests/system/presenter/test_search_for_id_by_external_id.py +++ b/tests/system/presenter/test_search_for_id_by_external_id.py @@ -132,3 +132,77 @@ def test_no_permission(self) -> None: data["message"], "Missing OrganizationManagementLevel: can_manage_organization", ) + + def test_with_locked_meeting(self) -> None: + self.set_models( + { + "user/1": { + "organization_management_level": OrganizationManagementLevel.CAN_MANAGE_ORGANIZATION, + }, + "meeting/2": { + "name": "test meeting", + "external_id": "234ex", + "committee_id": 11, + "locked_from_inside": True, + }, + "group/8": { + "name": "test group", + "external_id": "345ex", + "meeting_id": 2, + }, + "committee/11": { + "name": "test committee", + "external_id": "234ex", + "organization_id": 1, + }, + } + ) + + status_code, data = self.request( + "search_for_id_by_external_id", + {"collection": "meeting", "external_id": "234ex", "context_id": 11}, + ) + self.assertEqual(status_code, 200) + self.assertEqual(data, {"id": 2}) + + status_code, data = self.request( + "search_for_id_by_external_id", + {"collection": "group", "external_id": "345ex", "context_id": 2}, + ) + self.assertEqual(status_code, 200) + self.assertEqual(data, {"id": None, "error": "No item with '345ex' was found."}) + + status_code, data = self.request( + "search_for_id_by_external_id", + {"collection": "committee", "external_id": "234ex", "context_id": 1}, + ) + self.assertEqual(status_code, 200) + self.assertEqual(data, {"id": 11}) + + def test_one_of_many_groups_found_due_to_meeting_lock(self) -> None: + self.set_models( + { + "group/8": { + "name": "test group", + "external_id": "1ex", + "meeting_id": 1, + }, + "group/9": { + "name": "test group 2", + "external_id": "1ex", + "meeting_id": 2, + }, + "meeting/1": {"name": "test meeting 1", "group_ids": [8]}, + "meeting/2": { + "name": "test meeting 2", + "group_ids": [9], + "locked_from_inside": True, + }, + } + ) + status_code, data = self.request( + "search_for_id_by_external_id", + {"collection": "group", "external_id": "1ex", "context_id": 1}, + ) + self.assertEqual(status_code, 200) + self.assertEqual(data, {"id": 8})