Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
1 change: 1 addition & 0 deletions changelog.d/3-bug-fixes/removal-client-check
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix bug in MLS user removal from conversation: the list of removed clients has to be compared with those in the conversation, not the list of *all* clients of that user
34 changes: 14 additions & 20 deletions services/galley/src/Galley/API/MLS/Message.hs
Original file line number Diff line number Diff line change
Expand Up @@ -1041,7 +1041,7 @@ executeProposalAction qusr con lconv cm action = do
-- final set of clients in the conversation
let clients = Set.map fst (newclients <> Map.findWithDefault mempty qtarget cm)
-- get list of mls clients from brig
clientInfo <- getMLSClients lconv qtarget ss
clientInfo <- getClientInfo lconv qtarget ss
let allClients = Set.map ciId clientInfo
let allMLSClients = Set.map ciId (Set.filter ciMLS clientInfo)
-- We check the following condition:
Expand All @@ -1062,7 +1062,7 @@ executeProposalAction qusr con lconv cm action = do
-- FUTUREWORK: turn this error into a proper response
throwS @'MLSClientMismatch

membersToRemove <- catMaybes <$> for removeUserClients (uncurry (checkRemoval lconv ss))
membersToRemove <- catMaybes <$> for removeUserClients (uncurry checkRemoval)

-- add users to the conversation and send events
addEvents <- foldMap addMembers . nonEmpty . map fst $ newUserClients
Expand All @@ -1083,25 +1083,19 @@ executeProposalAction qusr con lconv cm action = do
-- This also filters out client removals for clients that don't exist anymore
-- For these clients there is nothing left to do
checkRemoval ::
Local x ->
SignatureSchemeTag ->
Qualified UserId ->
Set (ClientId, KeyPackageRef) ->
Sem r (Maybe (Qualified UserId))
checkRemoval loc ss qtarget (Set.map fst -> clients) = do
allClients <- Set.map ciId <$> getMLSClients loc qtarget ss
let allClientsDontExist = Set.null (clients `Set.intersection` allClients)
if allClientsDontExist
then pure Nothing
else do
-- We only support removal of client for user. This is likely to change in the future.
-- See discussions here https://wearezeta.atlassian.net/wiki/spaces/CL/pages/612106259/Relax+constraint+between+users+and+clients+in+MLS+groups
when (clients /= allClients) $ do
-- FUTUREWORK: turn this error into a proper response
throwS @'MLSClientMismatch
when (qusr == qtarget) $
throwS @'MLSSelfRemovalNotAllowed
pure (Just qtarget)
checkRemoval qtarget (Set.map fst -> clients) = do
let clientsInConv = Set.map fst (Map.findWithDefault mempty qtarget cm)
-- We only support removal of client for user. This is likely to change in the future.
-- See discussions here https://wearezeta.atlassian.net/wiki/spaces/CL/pages/612106259/Relax+constraint+between+users+and+clients+in+MLS+groups
when (clients /= clientsInConv) $ do
-- FUTUREWORK: turn this error into a proper response
throwS @'MLSClientMismatch
when (qusr == qtarget) $
throwS @'MLSSelfRemovalNotAllowed
pure (Just qtarget)

existingLocalMembers :: Set (Qualified UserId)
existingLocalMembers =
Expand Down Expand Up @@ -1144,13 +1138,13 @@ executeProposalAction qusr con lconv cm action = do
handleNoChanges :: Monoid a => Sem (Error NoChanges ': r) a -> Sem r a
handleNoChanges = fmap fold . runError

getMLSClients ::
getClientInfo ::
Members '[BrigAccess, FederatorAccess] r =>
Local x ->
Qualified UserId ->
SignatureSchemeTag ->
Sem r (Set ClientInfo)
getMLSClients loc = foldQualified loc getLocalMLSClients getRemoteMLSClients
getClientInfo loc = foldQualified loc getLocalMLSClients getRemoteMLSClients

getRemoteMLSClients ::
Member FederatorAccess r =>
Expand Down
1 change: 1 addition & 0 deletions services/galley/test/integration/API/MLS.hs
Original file line number Diff line number Diff line change
Expand Up @@ -768,6 +768,7 @@ testAdminRemovesUserFromConv = do
[alice, bob] <- createAndConnectUsers [Nothing, Nothing]
(qcnv, events) <- runMLSTest $ do
[alice1, bob1, bob2] <- traverse createMLSClient [alice, bob, bob]
void $ createWireClient bob -- also create one extra non-MLS client
traverse_ uploadNewKeyPackage [bob1, bob2]
(_, qcnv) <- setupMLSGroup alice1
void $ createAddCommit alice1 [bob] >>= sendAndConsumeCommit
Expand Down