From ca7ddc727863f6d55aeed57a072afc1c892a21ec Mon Sep 17 00:00:00 2001 From: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com> Date: Thu, 28 May 2026 14:37:53 +0400 Subject: [PATCH] wip --- src/Simplex/Chat/Library/Internal.hs | 4 ++-- src/Simplex/Chat/Library/Subscriber.hs | 4 ++-- src/Simplex/Chat/Store/Groups.hs | 11 +++++++++++ tests/ChatTests/Groups.hs | 7 ------- 4 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/Simplex/Chat/Library/Internal.hs b/src/Simplex/Chat/Library/Internal.hs index f4078dd111..0086f6f12b 100644 --- a/src/Simplex/Chat/Library/Internal.hs +++ b/src/Simplex/Chat/Library/Internal.hs @@ -2143,7 +2143,7 @@ bumpAndBroadcastRoster user gInfo let rosterVer = maybe 0 (+ 1) (rosterVersion gInfo) (relays, roster) <- withStore' $ \db -> do relays <- getGroupRelayMembers db vr user gInfo - mods <- getGroupModerators db vr user gInfo + mods <- getGroupModsNoOwners db vr user gInfo setGroupRosterVersion db gInfo rosterVer pure (relays, buildGroupRoster rosterVer mods) forM_ (L.nonEmpty relays) $ \relays' -> @@ -2154,7 +2154,7 @@ sendGroupRosterToRelay :: User -> GroupInfo -> GroupMember -> CM () sendGroupRosterToRelay user gInfo relayMember = forM_ (rosterVersion gInfo) $ \rosterVer -> do vr <- chatVersionRange - mods <- withStore' $ \db -> getGroupModerators db vr user gInfo + mods <- withStore' $ \db -> getGroupModsNoOwners db vr user gInfo void $ sendGroupMessage' user gInfo [relayMember] (XGrpRoster (buildGroupRoster rosterVer mods)) sendGroupMessages :: MsgEncodingI e => User -> GroupInfo -> Maybe GroupChatScope -> ShowGroupAsSender -> [GroupMember] -> NonEmpty (ChatMsgEvent e) -> CM (NonEmpty (Either ChatError SndMessage), GroupSndResult) diff --git a/src/Simplex/Chat/Library/Subscriber.hs b/src/Simplex/Chat/Library/Subscriber.hs index 7a59233c5a..2eafb972b3 100644 --- a/src/Simplex/Chat/Library/Subscriber.hs +++ b/src/Simplex/Chat/Library/Subscriber.hs @@ -3176,9 +3176,9 @@ processAgentMessageConn vr user@User {userId} corrId agentConnId agentMessage = warnings <- withStore' $ \db -> do warnings <- forM entries (applyRosterEntryDB db) -- absent privileged members revert to the joiner default - currentPriv <- getGroupModerators db vr user gInfo + currentPriv <- getGroupModsNoOwners db vr user gInfo forM_ currentPriv $ \m -> - when (isRosterRole (memberRole' m) && memberId' m `notElem` rosterIds) $ + when (memberId' m `notElem` rosterIds) $ updateGroupMemberRole db user m defaultRole pure warnings sequence_ warnings diff --git a/src/Simplex/Chat/Store/Groups.hs b/src/Simplex/Chat/Store/Groups.hs index b7915861bb..c9173a2437 100644 --- a/src/Simplex/Chat/Store/Groups.hs +++ b/src/Simplex/Chat/Store/Groups.hs @@ -67,6 +67,7 @@ module Simplex.Chat.Store.Groups getGroupMembersByIndexes, getSupportScopeMembersByIndexes, getGroupModerators, + getGroupModsNoOwners, getGroupRelayMembers, getGroupMembersForExpiration, getRemovedMembersToCleanup, @@ -1204,6 +1205,16 @@ getGroupModerators db vr user@User {userId, userContactId} GroupInfo {groupId} = (groupMemberQuery <> " WHERE m.user_id = ? AND m.group_id = ? AND (m.contact_id IS NULL OR m.contact_id != ?) AND m.member_role IN (?,?,?)") (userId, groupId, userContactId, GRModerator, GRAdmin, GROwner) +-- Moderators and admins only, excluding owners. Use for roster-related paths +-- where owners must not be touched (owners are link-anchored, not roster-managed). +getGroupModsNoOwners :: DB.Connection -> VersionRangeChat -> User -> GroupInfo -> IO [GroupMember] +getGroupModsNoOwners db vr user@User {userId, userContactId} GroupInfo {groupId} = do + map (toContactMember vr user) + <$> DB.query + db + (groupMemberQuery <> " WHERE m.user_id = ? AND m.group_id = ? AND (m.contact_id IS NULL OR m.contact_id != ?) AND m.member_role IN (?,?)") + (userId, groupId, userContactId, GRModerator, GRAdmin) + getGroupRelayMembers :: DB.Connection -> VersionRangeChat -> User -> GroupInfo -> IO [GroupMember] getGroupRelayMembers db vr user@User {userId, userContactId} GroupInfo {groupId} = do map (toContactMember vr user) diff --git a/tests/ChatTests/Groups.hs b/tests/ChatTests/Groups.hs index 4c6760f253..f0b98d1310 100644 --- a/tests/ChatTests/Groups.hs +++ b/tests/ChatTests/Groups.hs @@ -9487,11 +9487,6 @@ testChannelBlockMemberSigned ps = r2 `shouldStartWith` "blocked" r2 `shouldEndWith` "(signed)" --- End-to-end validation of the owner-signed roster: a member promoted to --- moderator performs a SIGNED administrative action (block), and another member --- who learned that moderator's key, name, and role ONLY from the roster (never --- from a relay-asserted introduction) verifies and honors it. Without the --- roster, eve would have no trusted key for cath and would reject the block. testChannelModeratorActionViaRoster :: HasCallStack => TestParams -> IO () testChannelModeratorActionViaRoster ps = withNewTestChat ps "alice" aliceProfile $ \alice -> @@ -9539,8 +9534,6 @@ testChannelModeratorActionViaRoster ps = dan <##. "#team: unknown member cath" dan <##. "#team: bob introduced cath" --- Removing a moderator triggers anyPrivilegedRemoved → bumpAndBroadcastRoster on the --- owner, which refreshes the relay's cached roster (the broadcast is silent on members). testChannelRemovedModeratorRefreshesRoster :: HasCallStack => TestParams -> IO () testChannelRemovedModeratorRefreshesRoster ps = withNewTestChat ps "alice" aliceProfile $ \alice ->