Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow the orgaadmin to do more #2744

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
9 changes: 8 additions & 1 deletion openslides_backend/action/actions/meeting/clone.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
external_motion_fields,
)
from openslides_backend.models.models import Meeting, MeetingUser
from openslides_backend.permissions.permission_helper import has_perm
from openslides_backend.permissions.permissions import Permissions
from openslides_backend.services.datastore.interface import GetManyRequest
from openslides_backend.shared.exceptions import ActionException, PermissionDenied
from openslides_backend.shared.interfaces.event import Event, EventType
Expand Down Expand Up @@ -109,7 +111,12 @@ 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"):
if meeting.get("locked_from_inside") and not has_perm(
self.datastore,
self.user_id,
Permissions.Meeting.CAN_MANAGE_SETTINGS,
instance["meeting_id"],
):
raise ActionException("Cannot clone locked meeting.")

if committee_id := instance.get("committee_id"):
Expand Down
26 changes: 24 additions & 2 deletions openslides_backend/action/actions/meeting/mixins.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
from typing import Any, cast

from ....permissions.management_levels import CommitteeManagementLevel
from ....permissions.permission_helper import has_committee_management_level
from ....permissions.management_levels import (
CommitteeManagementLevel,
OrganizationManagementLevel,
)
from ....permissions.permission_helper import has_committee_management_level, has_perm
from ....permissions.permissions import Permissions
from ....shared.exceptions import ActionException, MissingPermission
from ....shared.patterns import fqid_from_collection_and_id
from ...action import Action
Expand All @@ -23,6 +27,24 @@ def validate_instance(self, instance: dict[str, Any]) -> None:

def check_permissions(self, instance: dict[str, Any]) -> None:
committee_id = self.get_committee_id(instance)
if (
(id_ := (instance.get("id") or instance.get("meeting_id")))
and self.datastore.get(
fqid_from_collection_and_id("meeting", id_), ["locked_from_inside"]
).get("locked_from_inside")
and self.datastore.get(
fqid_from_collection_and_id("user", self.user_id),
["organization_management_level"],
).get("organization_management_level")
!= OrganizationManagementLevel.SUPERADMIN
):
if not has_perm(
self.datastore,
self.user_id,
Permissions.Meeting.CAN_MANAGE_SETTINGS,
id_,
):
raise MissingPermission({Permissions.Meeting.CAN_MANAGE_SETTINGS: id_})
if not has_committee_management_level(
self.datastore,
self.user_id,
Expand Down
8 changes: 4 additions & 4 deletions openslides_backend/permissions/permission_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@ def has_perm(
],
lock_result=False,
)
if (
user.get("organization_management_level")
== OrganizationManagementLevel.SUPERADMIN
):
if user.get("organization_management_level") in [
OrganizationManagementLevel.SUPERADMIN,
OrganizationManagementLevel.CAN_MANAGE_ORGANIZATION,
]:
return True

meeting_user = get_meeting_user(
Expand Down
13 changes: 13 additions & 0 deletions tests/system/action/agenda_item/test_assign.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,3 +155,16 @@ def test_assign_permissions_with_locked_meeting(self) -> None:
True,
lock_meeting=True,
)

def test_assign_permissions_with_locked_meeting_orgaadmin(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.CAN_MANAGE_ORGANIZATION,
True,
lock_meeting=True,
)
22 changes: 21 additions & 1 deletion tests/system/action/meeting/test_clone.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

from openslides_backend.action.action_worker import ActionWorkerState
from openslides_backend.models.models import AgendaItem, Meeting
from openslides_backend.permissions.permissions import Permissions
from openslides_backend.shared.util import (
ONE_ORGANIZATION_FQID,
ONE_ORGANIZATION_ID,
Expand Down Expand Up @@ -2008,7 +2009,7 @@ def test_clone_datastore_calls(self) -> None:
with CountDatastoreCalls() as counter:
response = self.request("meeting.clone", {"meeting_id": 1})
self.assert_status_code(response, 200)
assert counter.calls == 33
assert counter.calls == 34

@performance
def test_clone_performance(self) -> None:
Expand Down Expand Up @@ -2081,6 +2082,25 @@ def test_permissions_oml_locked_meeting(self) -> None:
self.assert_status_code(response, 400)
assert "Cannot clone locked meeting." in response.json["message"]

def test_permissions_oml_locked_meeting_with_can_manage_settings(self) -> None:
self.set_models(self.test_models)
bob_id = self.create_user("bob")
self.set_models(
{
"meeting/1": {
"locked_from_inside": True,
"template_for_organization_id": 1,
},
ONE_ORGANIZATION_FQID: {"template_meeting_ids": [1]},
}
)
self.set_group_permissions(1, [Permissions.Meeting.CAN_MANAGE_SETTINGS])
self.set_user_groups(1, [1])
response = self.request(
"meeting.clone", {"meeting_id": 1, "admin_ids": [bob_id]}
)
self.assert_status_code(response, 200)

def test_clone_require_duplicate_from_allowed(self) -> None:
self.set_models(self.test_models_with_admin)
self.set_models(
Expand Down
42 changes: 42 additions & 0 deletions tests/system/action/meeting/test_delete.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from typing import Any

from openslides_backend.permissions.management_levels import OrganizationManagementLevel
from openslides_backend.permissions.permissions import Permissions
from openslides_backend.shared.util import ONE_ORGANIZATION_FQID, ONE_ORGANIZATION_ID
from tests.system.action.base import BaseActionTestCase

Expand Down Expand Up @@ -429,6 +430,47 @@ def test_delete_with_locked_meeting(self) -> None:
lock_meeting=True,
)

def test_delete_with_locked_meeting_orgaadmin(self) -> None:
self.base_permission_test(
{},
"meeting.delete",
{"id": 1},
OrganizationManagementLevel.CAN_MANAGE_ORGANIZATION,
True,
lock_meeting=True,
)

def test_delete_permissions_can_manage_organization_with_locked_meeting_not_allowed(
self,
) -> None:
self.set_models(
{
"user/1": {"organization_management_level": "can_manage_organization"},
"meeting/1": {"locked_from_inside": True},
}
)
response = self.request("meeting.delete", {"id": 1})
self.assert_status_code(response, 403)
self.assertIn(
"You are not allowed to perform action meeting.delete. Missing permission: Permission meeting.can_manage_settings in meeting 1",
response.json["message"],
)

def test_delete_permissions_can_manage_organization_with_locked_meeting(
self,
) -> None:
self.set_models(
{
"user/1": {"organization_management_level": "can_manage_organization"},
"meeting/1": {"locked_from_inside": True},
}
)
self.set_group_permissions(11, [Permissions.Meeting.CAN_MANAGE_SETTINGS])
self.set_user_groups(1, [11])
response = self.request("meeting.delete", {"id": 1})
self.assert_status_code(response, 200)
self.assert_model_deleted("meeting/1")

def test_delete_with_public_orga_file(self) -> None:
self.set_models(
{
Expand Down
Loading