core: allow moderators to change member <-> observer roles in groups (#7279)

This commit is contained in:
spaced4ndy
2026-07-20 12:56:23 +00:00
committed by GitHub
parent bd55fedc2d
commit ba6f5386c8
3 changed files with 12 additions and 1 deletions
+3
View File
@@ -2884,6 +2884,9 @@ processChatCommand cxt nm = \case
when (length memberIds > 1 && (anyAdmin || newRole >= GRAdmin)) $
throwCmdError "can't change role of multiple members when admins selected, or new role is admin"
when anyPending $ throwCmdError "can't change role of members pending approval"
-- TODO allow moderators (recipients already accept it; needs UI too): the observer..member limit is an
-- TODO `all` over targets - maxRole can't express it, a max hides targets below GRObserver (relay,
-- TODO unknown) that receivers reject. Fold roleRequiredToChange per target, or add allModeratable.
assertUserGroupRole gInfo $ maximum ([GRAdmin, maxRole, newRole] :: [GroupMemberRole])
-- in relay groups the roster has a single signer, so only the owner may change member/moderator/admin roles
when (useRelays' gInfo && (isRosterRole newRole || anyPrivilegedTarget) && memberRole' (membership gInfo) /= GROwner) $
+8
View File
@@ -1291,6 +1291,14 @@ isRosterRole r = r == GRMember || r == GRModerator || r == GRAdmin
isPrivilegedRole :: GroupMemberRole -> Bool
isPrivilegedRole r = r >= GRMember
-- Minimum role allowed to change a member's role from `from` to `to` (moderators only within observer..member).
roleRequiredToChange :: GroupMemberRole -> GroupMemberRole -> GroupMemberRole
roleRequiredToChange from to
| moderatable from && moderatable to = GRModerator
| otherwise = maximum ([GRAdmin, from, to] :: [GroupMemberRole])
where
moderatable r = GRObserver <= r && r <= GRMember
-- Drop non-privileged-role entries and de-duplicate by memberId, keeping the first.
-- Runs on the parsed roster blob.
validateGroupRoster :: [RosterMember] -> [RosterMember]
+1 -1
View File
@@ -3363,7 +3363,7 @@ processAgentMessageConn cxt user@User {userId} corrId agentConnId agentMessage =
-- applyMember writes the change (role, or role + pinned key for a freshly TOFU-created member);
-- the delivery scope (relay forwarding) is computed on the pre-change role
changeMemberRole gInfo' member@GroupMember {memberRole = fromRole} created applyMember gEvent createItem
| senderRole < maximum ([GRAdmin, fromRole, memRole] :: [GroupMemberRole]) =
| senderRole < roleRequiredToChange fromRole memRole =
messageError "x.grp.mem.role with insufficient member permissions" $> Nothing
| useRelays' gInfo && (isRosterRole memRole || isRosterRole fromRole) && senderRole /= GROwner =
messageError "x.grp.mem.role: only the owner can change member, moderator and admin roles in relay groups" $> Nothing