directory: identify registration owner by member id to fix incorrect de-listing

The leave/removed/role-changed handlers identified the registration owner by
contact id. A non-owner member can be associated with the owner's contact (via
the contact/member merge), so its departure incorrectly de-listed the group.
Compare by group member id (owner_member_id) instead, falling back to contact
id for registrations recorded before owner_member_id existed.
This commit is contained in:
Narasimha-sc
2026-08-11 10:34:43 +00:00
parent ec6e975001
commit fc7dddbeda
3 changed files with 80 additions and 18 deletions
+53
View File
@@ -13,6 +13,7 @@ import ChatTests.Utils
import Control.Concurrent (forkIO, killThread, threadDelay)
import Control.Exception (finally)
import Control.Monad (forM_, when)
import Data.List (isInfixOf)
import qualified Data.Aeson as J
import qualified Data.Text as T
import Directory.Captcha
@@ -53,6 +54,7 @@ directoryServiceTests = do
it "should de-list if owner is removed from the group" testDelistedOwnerRemoved
it "should NOT de-list if another member leaves the group" testNotDelistedMemberLeaves
it "should NOT de-list if another member is removed from the group" testNotDelistedMemberRemoved
it "should NOT de-list if the owner rejoins via the group link and leaves the second membership" testNotDelistedOwnerRejoinsViaLink
it "should de-list if service is removed from the group" testDelistedServiceRemoved
it "should de-list if group is deleted" testDelistedGroupDeleted
it "should de-list/re-list when service/owner roles change" testDelistedRoleChanges
@@ -702,6 +704,57 @@ testNotDelistedMemberRemoved ps =
cath #> "@'SimpleX Directory_1' privacy"
groupFoundN_ "_1" Nothing 2 cath "privacy"
-- Reproduces the de-listing bug where a non-owner member associated with the
-- registration owner's contact (via the probe-and-merge mechanism) de-lists the
-- group when it leaves. The owner joins the directory-managed link a second time
-- (a single client owning both connection ends completes the merge with no
-- modified client), then leaves that second membership while remaining the owner.
testNotDelistedOwnerRejoinsViaLink :: HasCallStack => TestParams -> IO ()
testNotDelistedOwnerRejoinsViaLink ps =
withDirectoryService ps $ \superUser dsLink ->
withNewTestChat ps "bob" bobProfile $ \bob -> do
bob `connectVia` dsLink
submitGroup bob "privacy" "Privacy"
welcomeWithLink <- groupAccepted bob "privacy" 1
completeRegistration superUser bob "privacy" "Privacy" welcomeWithLink 1
let groupLink = dropStrPrefix "Link to join the group privacy: " welcomeWithLink
-- turn off the captcha filter so the owner's re-join is not screened
bob #> "@'SimpleX Directory' /filter 1 off"
bob <# "'SimpleX Directory'> > /filter 1 off"
bob <## " Spam filter settings for group privacy set to:"
bob <## "- reject long/inappropriate names: disabled"
bob <## "- pass captcha to join: disabled"
bob <## ""
bob <## "/'filter 1 name' - enable name filter"
bob <## "/'filter 1 captcha' - enable captcha challenge"
bob <## "/'filter 1 name captcha' - enable both"
-- the registration owner connects to the directory-managed link again,
-- creating a second membership that the probe-and-merge mechanism
-- associates with the owner's own contact on the directory service
bob ##> ("/c " <> groupLink)
bob <## "connection request sent!"
bob <## "#privacy_1: joining the group..."
bob <## "#privacy_1: you joined the group"
bob
<### [ "#privacy: 'SimpleX Directory' added bob_1 (Bob) to the group (connecting...)",
"contact and member are merged: 'SimpleX Directory', #privacy_1 'SimpleX Directory_1'",
"use @'SimpleX Directory' <message> to send messages",
Predicate ("Link to join the group privacy" `isInfixOf`),
"#privacy_1: member bob_2 (Bob) is connected",
"#privacy: new member bob_1 is connected"
]
-- allow the directory service to complete the contact/member merge that
-- associates the second membership (bob_1) with bob's contact
threadDelay 3000000
-- owner leaves the second membership, which is not the owner member
bob ##> "/l privacy_1"
bob <## "#privacy_1: you left the group"
bob <## "use /d #privacy_1 to delete the group"
bob <## "#privacy: bob_1 left the group"
-- the group must remain listed: the leaving member is not the owner member
(superUser </)
groupFound bob "privacy"
testDelistedServiceRemoved :: HasCallStack => TestParams -> IO ()
testDelistedServiceRemoved ps =
withDirectoryService ps $ \superUser dsLink ->