Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion changelog.d/2-features/subconv-leave
Original file line number Diff line number Diff line change
@@ -1 +1 @@
Implement endpoint for leaving a subconversation
Implement endpoint for leaving a subconversation (#2969, #3080)
9 changes: 6 additions & 3 deletions services/galley/src/Galley/API/MLS/Message.hs
Original file line number Diff line number Diff line change
Expand Up @@ -357,7 +357,8 @@ postMLSCommitBundleToLocalConv qusr mc conn bundle lConvOrSubId = do
ApplicationMessage _ -> throwS @'MLSUnsupportedMessage
ProposalMessage _ -> throwS @'MLSUnsupportedMessage

propagateMessage qusr lConvOrSub conn (rmRaw (cbCommitMsg bundle))
let cm = membersConvOrSub (tUnqualified lConvOrSub)
propagateMessage qusr lConvOrSub conn (rmRaw (cbCommitMsg bundle)) cm

for_ (cbWelcome bundle) $
postMLSWelcome lConvOrSub conn
Expand Down Expand Up @@ -574,7 +575,8 @@ postMLSMessageToLocalConv qusr senderClient con smsg convOrSubId = case rmValue
Right ApplicationMessageTag -> pure mempty
Left _ -> throwS @'MLSUnsupportedMessage

propagateMessage qusr lConvOrSub con (rmRaw smsg)
let cm = membersConvOrSub (tUnqualified lConvOrSub)
propagateMessage qusr lConvOrSub con (rmRaw smsg) cm

pure events

Expand Down Expand Up @@ -833,7 +835,8 @@ processExternalCommit qusr mSenderClient lConvOrSub epoch action updatePath = wi
-- fetch backend remove proposals of the previous epoch
kpRefs <- getPendingBackendRemoveProposals (cnvmlsGroupId . mlsMetaConvOrSub . tUnqualified $ lConvOrSub') epoch
-- requeue backend remove proposals for the current epoch
createAndSendRemoveProposals lConvOrSub' kpRefs qusr
let cm = membersConvOrSub (tUnqualified lConvOrSub')
createAndSendRemoveProposals lConvOrSub' kpRefs qusr cm
where
derefUser :: ClientMap -> Qualified UserId -> Sem r (ClientIdentity, KeyPackageRef)
derefUser cm user = case Map.assocs cm of
Expand Down
24 changes: 15 additions & 9 deletions services/galley/src/Galley/API/MLS/Propagate.hs
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,17 @@ propagateMessage ::
Local ConvOrSubConv ->
Maybe ConnId ->
ByteString ->
-- | The client map that has all the recipients of the message. This is an
-- argument, and not constructed within the function, because of a special
-- case of subconversations where everyone but the subconversation leaver
-- client should get the remove proposal message; in this case the recipients
-- are a strict subset of all the clients represented by the in-memory
-- conversation/subconversation client maps.
ClientMap ->
Sem r ()
propagateMessage qusr lConvOrSub con raw = do
propagateMessage qusr lConvOrSub con raw cm = do
now <- input @UTCTime
let cm = membersConvOrSub (tUnqualified lConvOrSub)
mlsConv = convOfConvOrSub <$> lConvOrSub
let mlsConv = convOfConvOrSub <$> lConvOrSub
lmems = mcLocalMembers . tUnqualified $ mlsConv
botMap = Map.fromList $ do
m <- lmems
Expand All @@ -80,7 +86,7 @@ propagateMessage qusr lConvOrSub con raw = do
mkPush :: UserId -> ClientId -> MessagePush 'NormalMessage
mkPush u c = newMessagePush mlsConv botMap con mm (u, c) e
runMessagePush mlsConv (Just qcnv) $
foldMap (uncurry mkPush) (lmems >>= localMemberMLSClients mlsConv cm)
foldMap (uncurry mkPush) (lmems >>= localMemberMLSClients mlsConv)

-- send to remotes
traverse_ handleError
Expand All @@ -92,20 +98,20 @@ propagateMessage qusr lConvOrSub con raw = do
rmmSender = qusr,
rmmMetadata = mm,
rmmConversation = qUnqualified qcnv,
rmmRecipients = rs >>= remoteMemberMLSClients cm,
rmmRecipients = rs >>= remoteMemberMLSClients,
rmmMessage = Base64ByteString raw
}
where
localMemberMLSClients :: Local x -> ClientMap -> LocalMember -> [(UserId, ClientId)]
localMemberMLSClients loc cm lm =
localMemberMLSClients :: Local x -> LocalMember -> [(UserId, ClientId)]
localMemberMLSClients loc lm =
let localUserQId = tUntagged (qualifyAs loc localUserId)
localUserId = lmId lm
in map
(\(c, _) -> (localUserId, c))
(Map.assocs (Map.findWithDefault mempty localUserQId cm))

remoteMemberMLSClients :: ClientMap -> RemoteMember -> [(UserId, ClientId)]
remoteMemberMLSClients cm rm =
remoteMemberMLSClients :: RemoteMember -> [(UserId, ClientId)]
remoteMemberMLSClients rm =
let remoteUserQId = tUntagged (rmId rm)
remoteUserId = qUnqualified remoteUserQId
in map
Expand Down
17 changes: 13 additions & 4 deletions services/galley/src/Galley/API/MLS/Removal.hs
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,15 @@ createAndSendRemoveProposals ::
Local ConvOrSubConv ->
t KeyPackageRef ->
Qualified UserId ->
-- | The client map that has all the recipients of the message. This is an
-- argument, and not constructed within the function, because of a special
-- case of subconversations where everyone but the subconversation leaver
-- client should get the remove proposal message; in this case the recipients
-- are a strict subset of all the clients represented by the in-memory
-- conversation/subconversation client maps.
ClientMap ->
Sem r ()
createAndSendRemoveProposals lConvOrSubConv cs qusr = do
createAndSendRemoveProposals lConvOrSubConv cs qusr cm = do
let meta = mlsMetaConvOrSub (tUnqualified lConvOrSubConv)
mKeyPair <- getMLSRemovalKey
case mKeyPair of
Expand All @@ -86,7 +93,7 @@ createAndSendRemoveProposals lConvOrSubConv cs qusr = do
(proposalRef (cnvmlsCipherSuite meta) proposal)
ProposalOriginBackend
proposal
propagateMessage qusr lConvOrSubConv Nothing msgEncoded
propagateMessage qusr lConvOrSubConv Nothing msgEncoded cm

-- | Send remove proposals for a single client of a user to the local conversation.
removeClient ::
Expand All @@ -113,7 +120,8 @@ removeClient lc qusr cid = do
for_ mMlsConv $ \mlsConv -> do
-- FUTUREWORK: also remove the client from from subconversations of lc
let cidAndKPs = maybeToList (cmLookupRef (mkClientIdentity qusr cid) (mcMembers mlsConv))
createAndSendRemoveProposals (qualifyAs lc (Conv mlsConv)) cidAndKPs qusr
cm = mcMembers mlsConv
createAndSendRemoveProposals (qualifyAs lc (Conv mlsConv)) cidAndKPs qusr cm

-- | Send remove proposals for all clients of the user to the local conversation.
removeUser ::
Expand All @@ -139,4 +147,5 @@ removeUser lc qusr = do
for_ mMlsConv $ \mlsConv -> do
-- FUTUREWORK: also remove the client from from subconversations of lc
let kprefs = toList (Map.findWithDefault mempty qusr (mcMembers mlsConv))
createAndSendRemoveProposals (qualifyAs lc (Conv mlsConv)) kprefs qusr
cm = mcMembers mlsConv
createAndSendRemoveProposals (qualifyAs lc (Conv mlsConv)) kprefs qusr cm
3 changes: 3 additions & 0 deletions services/galley/src/Galley/API/MLS/SubConversation.hs
Original file line number Diff line number Diff line change
Expand Up @@ -412,10 +412,13 @@ leaveLocalSubConversation cid lcnv sub = do
kp <-
note (mlsProtocolError "Client is not a member of the subconversation") $
cmLookupRef cid (scMembers subConv)
-- remove the leaver from the member list
let cm = cmRemoveClient cid (scMembers subConv)
createAndSendRemoveProposals
(qualifyAs lcnv (SubConv mlsConv subConv))
(Identity kp)
(cidQualifiedUser cid)
cm

leaveRemoteSubConversation ::
( Members
Expand Down
7 changes: 7 additions & 0 deletions services/galley/src/Galley/API/MLS/Types.hs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,13 @@ cmLookupRef cid cm = do
clients <- Map.lookup (cidQualifiedUser cid) cm
Map.lookup (ciClient cid) clients

cmRemoveClient :: ClientIdentity -> ClientMap -> ClientMap
cmRemoveClient cid cm = case Map.lookup (cidQualifiedUser cid) cm of
Nothing -> cm
Just clients ->
let clients' = Map.delete (ciClient cid) clients
in Map.insert (cidQualifiedUser cid) clients' cm

isClientMember :: ClientIdentity -> ClientMap -> Bool
isClientMember ci = isJust . cmLookupRef ci

Expand Down
5 changes: 4 additions & 1 deletion services/galley/test/integration/API/MLS.hs
Original file line number Diff line number Diff line change
Expand Up @@ -2791,6 +2791,7 @@ testLeaveSubConv = do
(qsub, _) <- withTempMockFederator'
( receiveCommitMock [charlie1]
<|> welcomeMock
<|> ("on-mls-message-sent" ~> RemoteMLSMessageOk)
)
$ do
void $ createAddCommit alice1 [bob, charlie] >>= sendAndConsumeCommit
Expand All @@ -2805,7 +2806,7 @@ testLeaveSubConv = do
[bob1KP] <-
map snd . filter (\(cid, _) -> cid == bob1)
<$> getClientsFromGroupState alice1 bob
mlsBracket [alice1, bob2] $ \wss -> do
mlsBracket [bob1, alice1, bob2] $ \(wsBob1 : wss) -> do
(_, reqs) <- withTempMockFederator' messageSentMock $ leaveCurrentConv bob1 qsub
req <-
assertOne
Expand All @@ -2821,6 +2822,8 @@ testLeaveSubConv = do
WS.assertMatchN (5 # WS.Second) wss $
wsAssertBackendRemoveProposal bob qcnv bob1KP
traverse_ (uncurry consumeMessage1) (zip [alice1, bob2] msgs)
-- assert the leaver gets no proposal or event
void . liftIO $ WS.assertNoEvent (5 # WS.Second) [wsBob1]

-- alice commits the pending proposal
void $ createPendingProposalCommit alice1 >>= sendAndConsumeCommitBundle
Expand Down