Skip to content

Commit d7e4f84

Browse files
Add new setting to restrict same-lvl committee admin editing (#3263)
1 parent 4e5a732 commit d7e4f84

8 files changed

Lines changed: 243 additions & 3 deletions

File tree

docs/actions/committee.update.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@
1919

2020
// Group C
2121
parent_id: Id;
22+
23+
// Group D
24+
manager_ids: Id[];
2225
}
2326
```
2427

@@ -32,3 +35,4 @@ Re-calculates `committee/all_parent_ids` from the new `parent_id` for this and a
3235
- Group A: The user needs the CML `can_manage` or the OML `can_manage_organization`
3336
- Group B: The user needs the OML `can_manage_organization` or the CML `can_manage` for all target committees that were added/removed from the list and not `organization/restrict_edit_forward_committees` to be set.
3437
- Group C: The user needs the OML `can_manage_organization` or the CML `can_manage` for a committee that is an _ancestor_ of the intended child committee and either the intended parent committee or one of its ancestors. Only organization managers may set this field to `None`.
38+
- Group D: Like group A, except if `organization/restrict_editing_same_level_committee_admins` is true, the CML requirement will be further restricted to ancestor committee `can_manage`CMLs only. Users with no other admin permission than that of the edited committee will therefore not be allowed.

docs/actions/organization.update.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
users_email_body: text;
2020
require_duplicate_from: boolean;
2121
disable_forward_with_attachments: boolean;
22+
restrict_editing_same_level_committee_admins: boolean;
2223
restrict_edit_forward_committees: boolean;
2324

2425
// Group B

meta

Submodule meta updated 1 file

openslides_backend/action/actions/committee/update.py

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from ....services.datastore.commands import GetManyRequest
1313
from ....shared.exceptions import ActionException, MissingPermission
1414
from ....shared.patterns import fqid_from_collection_and_id
15+
from ....shared.util import ONE_ORGANIZATION_ID
1516
from ...generics.update import UpdateAction
1617
from ...util.default_schema import DefaultSchema
1718
from ...util.register import register_action
@@ -208,16 +209,42 @@ def check_permissions(self, instance: dict[str, Any]) -> None:
208209
}
209210
)
210211

212+
check_id: int = instance["id"]
213+
if "manager_ids" in instance:
214+
data = self.datastore.get_many(
215+
[
216+
GetManyRequest(
217+
"committee",
218+
[check_id],
219+
["parent_id"],
220+
),
221+
GetManyRequest(
222+
"organization",
223+
[ONE_ORGANIZATION_ID],
224+
["restrict_editing_same_level_committee_admins"],
225+
),
226+
],
227+
lock_result=False,
228+
)
229+
if data["organization"][ONE_ORGANIZATION_ID].get(
230+
"restrict_editing_same_level_committee_admins"
231+
):
232+
if not (parent_id := data["committee"][check_id].get("parent_id", 0)):
233+
raise MissingPermission(
234+
OrganizationManagementLevel.CAN_MANAGE_ORGANIZATION
235+
)
236+
check_id = parent_id
237+
211238
if has_committee_management_level(
212239
self.datastore,
213240
self.user_id,
214-
instance["id"],
241+
check_id,
215242
):
216243
return
217244

218245
raise MissingPermission(
219246
{
220247
OrganizationManagementLevel.CAN_MANAGE_ORGANIZATION: 1,
221-
CommitteeManagementLevel.CAN_MANAGE: instance["id"],
248+
CommitteeManagementLevel.CAN_MANAGE: check_id,
222249
}
223250
)

openslides_backend/action/actions/organization/update.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ class OrganizationUpdate(
3838
"users_email_body",
3939
"require_duplicate_from",
4040
"disable_forward_with_attachments",
41+
"restrict_editing_same_level_committee_admins",
4142
"restrict_edit_forward_committees",
4243
)
4344

openslides_backend/models/models.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ class Organization(Model):
4040
)
4141
require_duplicate_from = fields.BooleanField()
4242
enable_anonymous = fields.BooleanField()
43+
restrict_editing_same_level_committee_admins = fields.BooleanField()
4344
saml_enabled = fields.BooleanField()
4445
saml_login_button_text = fields.CharField(default="SAML login")
4546
saml_attr_mapping = fields.JSONField()

tests/system/action/committee/test_update.py

Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -632,6 +632,9 @@ def test_update_group_a_no_permission(self) -> None:
632632
)
633633

634634
def test_update_group_a_permission_1(self) -> None:
635+
"""
636+
Also tests the same for group d in case of the setting not being set.
637+
"""
635638
self.create_data()
636639
self.set_models(
637640
{
@@ -648,11 +651,15 @@ def test_update_group_a_permission_1(self) -> None:
648651
"name": "test",
649652
"description": "blablabla",
650653
"external_id": "test",
654+
"manager_ids": [20],
651655
},
652656
)
653657
self.assert_status_code(response, 200)
654658

655659
def test_update_group_a_permission_2(self) -> None:
660+
"""
661+
Also tests the same for group d in case of the setting not being set.
662+
"""
656663
self.create_data()
657664
self.set_models(
658665
{
@@ -673,13 +680,18 @@ def test_update_group_a_permission_2(self) -> None:
673680
"name": "test",
674681
"description": "blablabla",
675682
"external_id": "test",
683+
"manager_ids": [20],
676684
},
677685
)
678686
self.assert_status_code(response, 200)
679687

680688
def test_update_group_a_permission_parent_committee_admin(self) -> None:
689+
"""
690+
Also tests the same for group d in case of the setting not being set.
691+
"""
681692
self.create_committee(3)
682693
self.create_committee(4, parent_id=3)
694+
bob_id = self.create_user("bob")
683695
self.set_committee_management_level([3])
684696
self.set_organization_management_level(None)
685697
response = self.request(
@@ -689,14 +701,19 @@ def test_update_group_a_permission_parent_committee_admin(self) -> None:
689701
"name": "test",
690702
"description": "blablabla",
691703
"external_id": "test",
704+
"manager_ids": [1, bob_id],
692705
},
693706
)
694707
self.assert_status_code(response, 200)
695708

696709
def test_update_group_a_permission_grandparent_committee_admin(self) -> None:
710+
"""
711+
Also tests the same for group d in case of the setting not being set.
712+
"""
697713
self.create_committee(2)
698714
self.create_committee(3, parent_id=2)
699715
self.create_committee(4, parent_id=3)
716+
bob_id = self.create_user("bob")
700717
self.set_committee_management_level([2])
701718
self.set_organization_management_level(None)
702719
response = self.request(
@@ -706,6 +723,7 @@ def test_update_group_a_permission_grandparent_committee_admin(self) -> None:
706723
"name": "test",
707724
"description": "blablabla",
708725
"external_id": "test",
726+
"manager_ids": [1, bob_id],
709727
},
710728
)
711729
self.assert_status_code(response, 200)
@@ -1335,6 +1353,192 @@ def test_update_add_forwarding_relations_fail_forward_all(self) -> None:
13351353
fail_forward_to=True, fail_forward_from=True, fail_remove=True
13361354
)
13371355

1356+
def test_update_group_d_no_setting_no_permission(self) -> None:
1357+
self.create_data()
1358+
self.set_models(
1359+
{
1360+
"user/1": {
1361+
"organization_management_level": OrganizationManagementLevel.CAN_MANAGE_USERS
1362+
}
1363+
}
1364+
)
1365+
response = self.request(
1366+
"committee.update",
1367+
{"id": 1, "manager_ids": [20]},
1368+
)
1369+
self.assert_status_code(response, 403)
1370+
self.assertIn(
1371+
"You are not allowed to perform action committee.update. Missing permissions: OrganizationManagementLevel can_manage_organization in organization 1 or CommitteeManagementLevel can_manage in committee 1",
1372+
response.json["message"],
1373+
)
1374+
1375+
def test_update_group_d_no_permission(self) -> None:
1376+
self.create_data()
1377+
self.set_models(
1378+
{
1379+
ONE_ORGANIZATION_FQID: {
1380+
"restrict_editing_same_level_committee_admins": True
1381+
},
1382+
"user/1": {
1383+
"organization_management_level": OrganizationManagementLevel.CAN_MANAGE_USERS
1384+
},
1385+
}
1386+
)
1387+
response = self.request(
1388+
"committee.update",
1389+
{"id": 1, "manager_ids": [20]},
1390+
)
1391+
self.assert_status_code(response, 403)
1392+
self.assertIn(
1393+
"You are not allowed to perform action committee.update. Missing OrganizationManagementLevel: can_manage_organization",
1394+
response.json["message"],
1395+
)
1396+
1397+
def test_update_group_d_no_permission_with_parent(self) -> None:
1398+
self.create_data()
1399+
self.set_models(
1400+
{
1401+
ONE_ORGANIZATION_FQID: {
1402+
"restrict_editing_same_level_committee_admins": True
1403+
},
1404+
self.COMMITTEE_FQID: {"parent_id": 2, "all_parent_ids": [2]},
1405+
"committee/2": {
1406+
"child_ids": [self.COMMITTEE_ID],
1407+
"all_child_ids": [self.COMMITTEE_ID],
1408+
},
1409+
"user/1": {
1410+
"organization_management_level": OrganizationManagementLevel.CAN_MANAGE_USERS
1411+
},
1412+
}
1413+
)
1414+
response = self.request(
1415+
"committee.update",
1416+
{"id": 1, "manager_ids": [20]},
1417+
)
1418+
self.assert_status_code(response, 403)
1419+
self.assertIn(
1420+
"You are not allowed to perform action committee.update. Missing permissions: OrganizationManagementLevel can_manage_organization in organization 1 or CommitteeManagementLevel can_manage in committee 2",
1421+
response.json["message"],
1422+
)
1423+
1424+
def test_update_group_d_permission_1(self) -> None:
1425+
self.create_data()
1426+
self.set_models(
1427+
{
1428+
ONE_ORGANIZATION_FQID: {
1429+
"restrict_editing_same_level_committee_admins": True
1430+
},
1431+
"user/1": {
1432+
"organization_management_level": OrganizationManagementLevel.CAN_MANAGE_ORGANIZATION,
1433+
},
1434+
"committee/1": {"organization_id": 1},
1435+
}
1436+
)
1437+
response = self.request(
1438+
"committee.update",
1439+
{"id": 1, "manager_ids": [20]},
1440+
)
1441+
self.assert_status_code(response, 200)
1442+
1443+
def test_update_group_d_permission_2(self) -> None:
1444+
self.create_data()
1445+
self.set_models(
1446+
{
1447+
ONE_ORGANIZATION_FQID: {
1448+
"restrict_editing_same_level_committee_admins": True
1449+
},
1450+
"user/1": {
1451+
"committee_management_ids": [1],
1452+
},
1453+
"committee/1": {
1454+
"organization_id": 1,
1455+
"manager_ids": [1],
1456+
},
1457+
}
1458+
)
1459+
self.set_organization_management_level(None)
1460+
response = self.request(
1461+
"committee.update",
1462+
{"id": 1, "manager_ids": [20]},
1463+
)
1464+
self.assert_status_code(response, 403)
1465+
self.assertIn(
1466+
"You are not allowed to perform action committee.update. Missing OrganizationManagementLevel: can_manage_organization",
1467+
response.json["message"],
1468+
)
1469+
1470+
def test_update_group_d_permission_2_with_parent(self) -> None:
1471+
self.create_data()
1472+
self.set_models(
1473+
{
1474+
ONE_ORGANIZATION_FQID: {
1475+
"restrict_editing_same_level_committee_admins": True
1476+
},
1477+
"user/1": {
1478+
"committee_management_ids": [1],
1479+
},
1480+
"committee/1": {
1481+
"parent_id": 2,
1482+
"all_parent_ids": [2],
1483+
"organization_id": 1,
1484+
"manager_ids": [1],
1485+
},
1486+
"committee/2": {
1487+
"child_ids": [self.COMMITTEE_ID],
1488+
"all_child_ids": [self.COMMITTEE_ID],
1489+
},
1490+
}
1491+
)
1492+
self.set_organization_management_level(None)
1493+
response = self.request(
1494+
"committee.update",
1495+
{"id": 1, "manager_ids": [20]},
1496+
)
1497+
self.assert_status_code(response, 403)
1498+
self.assertIn(
1499+
"You are not allowed to perform action committee.update. Missing permissions: OrganizationManagementLevel can_manage_organization in organization 1 or CommitteeManagementLevel can_manage in committee 2",
1500+
response.json["message"],
1501+
)
1502+
1503+
def test_update_group_d_permission_parent_committee_admin(self) -> None:
1504+
self.create_committee(3)
1505+
self.create_committee(4, parent_id=3)
1506+
bob_id = self.create_user("bob")
1507+
self.set_committee_management_level([3])
1508+
self.set_models(
1509+
{
1510+
ONE_ORGANIZATION_FQID: {
1511+
"restrict_editing_same_level_committee_admins": True
1512+
}
1513+
}
1514+
)
1515+
self.set_organization_management_level(None)
1516+
response = self.request(
1517+
"committee.update",
1518+
{"id": 4, "manager_ids": [1, bob_id]},
1519+
)
1520+
self.assert_status_code(response, 200)
1521+
1522+
def test_update_group_d_permission_grandparent_committee_admin(self) -> None:
1523+
self.create_committee(2)
1524+
self.create_committee(3, parent_id=2)
1525+
self.create_committee(4, parent_id=3)
1526+
bob_id = self.create_user("bob")
1527+
self.set_committee_management_level([2])
1528+
self.set_models(
1529+
{
1530+
ONE_ORGANIZATION_FQID: {
1531+
"restrict_editing_same_level_committee_admins": True
1532+
}
1533+
}
1534+
)
1535+
self.set_organization_management_level(None)
1536+
response = self.request(
1537+
"committee.update",
1538+
{"id": 4, "manager_ids": [1, bob_id]},
1539+
)
1540+
self.assert_status_code(response, 200)
1541+
13381542
def test_update_restrict_forwarding(self) -> None:
13391543
self.create_committee()
13401544
self.create_committee(2)

tests/system/action/organization/test_update.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -252,6 +252,7 @@ def test_update_some_more_fields(self) -> None:
252252
"theme_id": 2,
253253
"reset_password_verbose_errors": False,
254254
"disable_forward_with_attachments": True,
255+
"restrict_editing_same_level_committee_admins": True,
255256
"enable_chat": True,
256257
"url": "https://openslides.example.com",
257258
"users_email_sender": "email_sender",
@@ -298,6 +299,7 @@ def test_update_some_more_fields(self) -> None:
298299
"theme_ids": [1, 2],
299300
"reset_password_verbose_errors": False,
300301
"disable_forward_with_attachments": True,
302+
"restrict_editing_same_level_committee_admins": True,
301303
"enable_chat": True,
302304
"url": "https://openslides.example.com",
303305
"users_email_sender": "email_sender",

0 commit comments

Comments
 (0)