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 meeting admin user to update a non admin user that shares all his meetings with requesting user. #2576

Open
wants to merge 24 commits into
base: main
Choose a base branch
from
Open
Changes from 4 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
c13c1f2
Allow meeting admin user to update a non admin user that shares all h…
hjanott Aug 15, 2024
130fbc9
prevent success on empty meetings and changing committee admin.
hjanott Aug 21, 2024
f2ac306
repair user create due to missing user id
hjanott Aug 21, 2024
a6d9665
formatting
hjanott Aug 21, 2024
56928aa
draft an idea for scope change in user scope mixin.
hjanott Aug 26, 2024
f4eec94
use function in field group methods and check_permissions for scop
hjanott Aug 28, 2024
86208da
Merge remote-tracking branch 'upstream/main' into 2522_meeting_admin_…
hjanott Aug 28, 2024
15a5365
better if else structure.
hjanott Aug 30, 2024
37ba259
Add get user editable presenter plus test.
hjanott Sep 4, 2024
7c24289
Extend get user editable with payload field names to support all payl…
hjanott Sep 9, 2024
3d0df2b
delete unnecessary comments and return types
hjanott Sep 11, 2024
e46cffe
restructure check_for_admin_in_all_meetings
hjanott Sep 19, 2024
2a3398f
merge main
hjanott Sep 19, 2024
a8085b1
running black and improving doc
hjanott Sep 19, 2024
9f42c46
delete unnecessary lines
hjanott Sep 19, 2024
84398e4
Merge branch 'main' into 2522_meeting_admin_can_manage_non_admin
Elblinator Sep 25, 2024
8ee6d4a
do requested changes
hjanott Oct 2, 2024
1d07d85
Merge branch 'main' of https://github.com/OpenSlides/openslides-backe…
hjanott Oct 7, 2024
e2e569d
coding style
hjanott Oct 7, 2024
e6fc78f
Improve method description
hjanott Oct 7, 2024
e2582d6
add plural to MissingPermission permission output
hjanott Oct 7, 2024
576b3f4
fix plural error in exception
hjanott Oct 8, 2024
29a0e48
change get editable presenter on a per field basis response and a per…
hjanott Oct 14, 2024
011f63d
Merge branch 'main' of https://github.com/OpenSlides/openslides-backe…
hjanott Oct 14, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 51 additions & 71 deletions openslides_backend/shared/mixins/user_scope_mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,13 +203,13 @@ def check_for_admin_in_all_meetings(
b_meeting_ids: set[int] | None = None,
) -> bool:
"""
This is used during a permission check for scope request, user.update/create with payload fields A and F and during
user altering actions like user.delete or set_default_password.
Checks if the requesting user has permissions to manage participants in all of requested users meetings
but requested user doesn't have any of these permissions. See backend issue 2522 on github for more details.
Also checks requested user has no committee management rights if a dict was provided instead of an id.
An ID will be provided during user deletion.
Returns true if permissions are given. False if not. Raises no Exceptions.
This function checks the special permission condition for scope request, user.update/create with
payload fields A and F and other user altering actions like user.delete or set_default_password.
This requires all of:
* requested user is no committee manager
* requested user doesn't have any admin/user.can_update/user.can_manage rights in his meetings
* requesting user has those permissions in all of those meetings
Returns True if permissions are given. False if not.
hjanott marked this conversation as resolved.
Show resolved Hide resolved
"""
if not self._check_not_committee_manager(instance_id):
return False
Expand All @@ -220,8 +220,7 @@ def check_for_admin_in_all_meetings(
return False
assert isinstance(intersection_meetings, dict)
admin_meeting_users = self._collect_admin_meeting_users(intersection_meetings)
meeting_to_admin_users = self._collect_admin_users(admin_meeting_users)
return self._analyze_meetings(meeting_to_admin_users, instance_id)
return self._analyze_meeting_admins(admin_meeting_users, instance_id)

def _check_not_committee_manager(self, instance_id: int) -> bool:
"""
Expand All @@ -245,15 +244,14 @@ def _collect_intersected_meetings(
Helper function used in method check_for_admin_in_all_meetings.
Takes the meeting ids to find intersections with the requesting users meetings. Returns False if this is not possible.
"""
if not b_meeting_ids:
if not (
b_meeting_ids := {
m_id
for m_ids in self.instance_committee_meeting_ids.values()
for m_id in m_ids
}
):
return False
if not b_meeting_ids and not (
b_meeting_ids := {
m_id
for m_ids in self.instance_committee_meeting_ids.values()
for m_id in m_ids
}
):
return False
# During participant import there is no permstore.
if hasattr(self, "permstore"):
a_meeting_ids = (
Expand Down Expand Up @@ -295,53 +293,47 @@ def _collect_admin_meeting_users(
for meeting_id, meeting_dict in intersection_meetings.items()
for group_id in meeting_dict.get("group_ids", [])
]
admin_group_ids = [
meeting_dict["admin_group_id"]
for meeting_dict in intersection_meetings.values()
]
return {
*{
mu_id
for group_id, group in self.datastore.get_many(
[
GetManyRequest(
"group", group_ids, ["meeting_user_ids", "permissions"]
)
],
lock_result=False,
)
.get("group", {})
.items()
if (
"user.can_update" in group.get("permissions", [])
or "user.can_manage" in group.get("permissions", [])
)
for mu_id in group.get("meeting_user_ids", [])
},
*{
mu_id
for group_id, group in self.datastore.get_many(
[GetManyRequest("group", admin_group_ids, ["meeting_user_ids"])],
lock_result=False,
)
.get("group", {})
.items()
for mu_id in group.get("meeting_user_ids", [])
},
mu_id
for group_id, group in self.datastore.get_many(
[
GetManyRequest(
"group",
group_ids,
[
"meeting_user_ids",
"permissions",
"admin_group_for_meeting_id",
],
)
],
lock_result=False,
)
.get("group", {})
.items()
if (
group.get("admin_group_for_meeting_id")
or "user.can_update" in group.get("permissions", [])
or "user.can_manage" in group.get("permissions", [])
)
for mu_id in group.get("meeting_user_ids", [])
}

def _collect_admin_users(self, meeting_user_ids: set[int]) -> dict[int, set[int]]:
def _analyze_meeting_admins(
self, admin_meeting_user_ids: set[int], requested_user_id: int
) -> bool:
"""
Helper function used in method check_for_admin_in_all_meetings.
Returns the corresponding users of the groups meeting_users in a defaultdict meeting_id: user_ids.
Compares the users of admin meeting users of all meetings with the ids of requested user and requesting user.
Requesting user must be admin in all meetings. Requested user cannot be admin in any.
"""
meeting_to_admin_user = defaultdict(set)
meeting_to_admin_user_ids = defaultdict(set)
for meeting_user in (
self.datastore.get_many(
[
GetManyRequest(
"meeting_user",
list(meeting_user_ids),
list(admin_meeting_user_ids),
["user_id", "meeting_id"],
)
],
Expand All @@ -350,22 +342,10 @@ def _collect_admin_users(self, meeting_user_ids: set[int]) -> dict[int, set[int]
.get("meeting_user", {})
.values()
):
meeting_to_admin_user[meeting_user["meeting_id"]].add(
meeting_to_admin_user_ids[meeting_user["meeting_id"]].add(
meeting_user["user_id"]
)
return meeting_to_admin_user

def _analyze_meetings(
self, meeting_to_admin_users: dict[int, set[int]], instance_id: int
) -> bool:
"""
Helper function used in method check_for_admin_in_all_meetings.
Compares the admin users of all meetings with the ids of requested user and requesting user.
Requesting user must be admin in all meetings. Requested user cannot be admin in any.
"""
for meeting_id, admin_users in meeting_to_admin_users.items():
if instance_id in admin_users:
return False
if self.user_id not in admin_users:
return False
return True
return not any(
requested_user_id in admin_users or self.user_id not in admin_users
for meeting_id, admin_users in meeting_to_admin_user_ids.items()
)