core: preserve profile peer type when connecting incognito (#7751)

* core: preserve profile peer type when connecting incognito

* Apply batched suggestions from code review

Co-authored-by: Evgeny <evgeny@poberezkin.com>

* fix

---------

Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
This commit is contained in:
EvgenyandEvgeny @ SimpleX Chat authored and GitHub committed 2026-10-18 08:51:33 +01:00
1 parent 139ce5f8cc
commit 1d0247b7dd
7 files changed
+58 -23

No files matched your search

+8 -8
View File
@@ -462,7 +462,7 @@ processChatCommand cxt nm = \case
checkValidName displayName
checkProfileImageSize image
checkProfileSize p
p@Profile {displayName} <- liftIO $ maybe generateRandomProfile pure profile
p@Profile {displayName} <- liftIO $ maybe (generateRandomProfile Nothing) pure profile
u <- asks currentUser
users <- withFastStore' getUsers
forM_ users $ \User {profile = LocalProfile {displayName = n}, activeUser, viewPwdHash, userChatRelay = userChatRelay'} -> do
@@ -2132,7 +2132,7 @@ processChatCommand cxt nm = \case
Welcome -> withUser $ pure . CRWelcome
APIAddContact userId incognito -> withUserId userId $ \user -> do
-- [incognito] generate profile for connection
incognitoProfile <- if incognito then Just <$> liftIO generateRandomProfile else pure Nothing
incognitoProfile <- if incognito then Just <$> liftIO (generateIncognitoProfile user) else pure Nothing
subMode <- chatReadVar subscriptionMode
(preparedReq, preparedParams@PreparedLinkParams {plpLinkKey = LinkKey linkKey}) <- withAgent $ \a -> prepareInvitationLink a (aUserId user) IKUsePQ Nothing
linkProfile <- presentUserBadge user incognitoProfile (Just $ PHLink linkKey) $ userProfileDirect user incognitoProfile Nothing True
@@ -2150,7 +2150,7 @@ processChatCommand cxt nm = \case
let PendingContactConnection {pccConnStatus, customUserProfileId, connLinkInv} = conn
case (pccConnStatus, customUserProfileId, incognito) of
(ConnNew, Nothing, True) -> do
incognitoProfile <- liftIO generateRandomProfile
incognitoProfile <- liftIO $ generateIncognitoProfile user
sLnk <- updatePCCShortLinkData conn $ userProfileDirect user (Just incognitoProfile) Nothing True
conn' <- withFastStore' $ \db -> do
pId <- createIncognitoProfile db user incognitoProfile
@@ -2329,7 +2329,7 @@ processChatCommand cxt nm = \case
publicMemberCount_ = (\PublicGroupData {publicMemberCount} -> publicMemberCount) <$> publicGroupData_
-- Prepare group record once before connecting to relays (updatePreparedRelayedGroup):
-- set group link info and incognito profile, generate and store membership keys
incognitoProfile <- if incognito then Just <$> liftIO generateRandomProfile else pure Nothing
incognitoProfile <- if incognito then Just <$> liftIO (generateIncognitoProfile user) else pure Nothing
let cReqHash = contactCReqHash $ CRContactUri crData {crScheme = SSSimplex} e2e
g'@(GIK gInfo' _) <- withFastStore $ \db -> do
g'@(GIK gInfo' _) <- updatePreparedRelayedGroup db cxt user gInfo mainCReq cReqHash incognitoProfile rootKey publicMemberCount_
@@ -3886,7 +3886,7 @@ processChatCommand cxt nm = \case
pqSup' = PQSupportOn
joinNewConn chatV = do
-- [incognito] generate profile to send
incognitoProfile <- if incognito then Just <$> liftIO generateRandomProfile else pure Nothing
incognitoProfile <- if incognito then Just <$> liftIO (generateIncognitoProfile user) else pure Nothing
(connId, binding) <- withAgent $ \a -> prepareConnectionToJoin a (aUserId user) True cReq pqSup'
let ccLink = CCLink cReq $ serverShortLink <$> sLnk_
conn <- withFastStore' $ \db -> createDirectConnection' db userId connId ccLink contactId_ ConnPrepared incognitoProfile subMode chatV pqSup'
@@ -3958,7 +3958,7 @@ processChatCommand cxt nm = \case
-- [incognito] generate profile to send, or use membership profile for relay groups
incognitoProfile_ <- case gInfo_ of
Just (Just (GIK gInfo _)) | useRelays' gInfo -> pure $ ExistingIncognito <$> incognitoMembershipProfile gInfo
_ -> if incognito then Just . NewIncognito <$> liftIO generateRandomProfile else pure Nothing
_ -> if incognito then Just . NewIncognito <$> liftIO (generateIncognitoProfile user) else pure Nothing
let incognitoProfile = fromIncognitoProfile <$> incognitoProfile_
subMode <- chatReadVar subscriptionMode
let sLnk' = serverShortLink <$> sLnk
@@ -3974,7 +3974,7 @@ processChatCommand cxt nm = \case
(connId, chatV, reqHeader) <- prepareContact user cReq pqSup
newXContactId <- XContactId <$> drgRandomBytes 16
-- [incognito] generate profile to send
incognitoProfile <- if incognito then Just <$> liftIO generateRandomProfile else pure Nothing
incognitoProfile <- if incognito then Just <$> liftIO (generateIncognitoProfile user) else pure Nothing
subMode <- chatReadVar subscriptionMode
let cReqHash = contactCReqHash cReq
conn <- withFastStore' $ \db -> createConnReqConnection db userId connId (Just $ PCEContact ct) cReq reqHeader cReqHash shortLink newXContactId (NewIncognito <$> incognitoProfile) Nothing subMode chatV pqSup
@@ -4345,7 +4345,7 @@ processChatCommand cxt nm = \case
when (isPublicGroup groupKeys && isJust (memberAdmission >>= review)) $ throwCmdError "Admission review is not supported in channels"
when (not (isPublicGroup groupKeys) && isJust publicGroup) $ throwCmdError "publicGroup is not allowed in groups"
-- [incognito] generate incognito profile for group membership
incognitoProfile <- if incognito then Just <$> liftIO generateRandomProfile else pure Nothing
incognitoProfile <- if incognito then Just <$> liftIO (generateIncognitoProfile user) else pure Nothing
withFastStore $ \db -> createNewGroup db cxt user gProfile incognitoProfile memberId groupKeys publicMemberCount_
createNewGroupItems :: User -> GroupInfo -> CM ()
createNewGroupItems user gInfo = do
+5 -2
View File
@@ -994,7 +994,7 @@ acceptContactRequest nm user@User {userId} UserContactRequest {agentInvitationId
let chatV = vr cxt `peerConnChatVersion` cReqChatVRange
(ct, conn, incognitoProfile, getPresHeader) <- case contactId_ of
Nothing -> do
incognitoProfile <- if incognito then Just . NewIncognito <$> liftIO generateRandomProfile else pure Nothing
incognitoProfile <- if incognito then Just . NewIncognito <$> liftIO (generateIncognitoProfile user) else pure Nothing
(connId, binding) <- withAgent $ \a -> prepareConnectionToAccept a (aUserId user) True invId pqSup'
(ct, conn) <- withStore' $ \db ->
createContactFromRequest db user userContactLinkId_ connId chatV cReqChatVRange cName profileId cp xContactId incognitoProfile subMode pqSup' False
@@ -1003,7 +1003,7 @@ acceptContactRequest nm user@User {userId} UserContactRequest {agentInvitationId
ct <- withFastStore $ \db -> getContact db cxt user contactId
case contactConn ct of
Nothing -> do
incognitoProfile <- if incognito then Just . NewIncognito <$> liftIO generateRandomProfile else pure Nothing
incognitoProfile <- if incognito then Just . NewIncognito <$> liftIO (generateIncognitoProfile user) else pure Nothing
(connId, binding) <- withAgent $ \a -> prepareConnectionToAccept a (aUserId user) True invId pqSup'
currentTs <- liftIO getCurrentTime
conn <- withStore' $ \db -> do
@@ -1336,6 +1336,9 @@ introduceInChannel cxt user g@(GIK gInfo _) subscriber@GroupMember {activeConn =
sendIntros ms = forM_ (L.nonEmpty $ map (memberIntroEvt gInfo) ms) $ \evts ->
sendGroupMemberMessages user g conn evts
generateIncognitoProfile :: User -> IO Profile
generateIncognitoProfile User {profile = LocalProfile {peerType}} = generateRandomProfile peerType
userProfileInGroup :: User -> GroupInfo -> Maybe Profile -> Profile
userProfileInGroup user g = userProfileInGroup' user (Just g)
{-# INLINE userProfileInGroup #-}
+1 -2
View File
@@ -57,7 +57,6 @@ import Simplex.Chat.Messages
import Simplex.Chat.Messages.Batch (batchDeliveryTasks1, batchProfiles, batchProfilesWithBody, encodeBinaryBatch, encodeFwdElement, maxBatchElementSize)
import Simplex.Chat.Messages.CIContent
import Simplex.Chat.Messages.CIContent.Events
import Simplex.Chat.ProfileGenerator (generateRandomProfile)
import Simplex.Chat.Protocol
import Simplex.Chat.Store
import Simplex.Chat.Store.Connections
@@ -1535,7 +1534,7 @@ processAgentMessageConn cxt user@User {userId} entity gks_ corrId agentConnId ag
Just AutoAccept {acceptIncognito} -> do
incognitoProfile <-
if not shortLinkDataSet && acceptIncognito
then Just . NewIncognito <$> liftIO generateRandomProfile
then Just . NewIncognito <$> liftIO (generateIncognitoProfile user)
else pure Nothing
ct' <- acceptContactRequestAsync user uclId ct ucr incognitoProfile
toView $ CEvtAcceptingContactRequest user ct'
+5 -4
View File
@@ -1,16 +1,17 @@
{-# LANGUAGE NamedFieldPuns #-}
{-# LANGUAGE OverloadedStrings #-}
module Simplex.Chat.ProfileGenerator where
import Data.Text (Text)
import Simplex.Chat.Types (Profile (..))
import Simplex.Chat.Types (ChatPeerType, Profile (..))
import System.Random (randomRIO)
generateRandomProfile :: IO Profile
generateRandomProfile = do
generateRandomProfile :: Maybe ChatPeerType -> IO Profile
generateRandomProfile peerType = do
adjective <- pick adjectives
noun <- pickNoun adjective 2
pure $ Profile {displayName = adjective <> noun, fullName = "", shortDescr = Nothing, description = Nothing, image = Nothing, contactLink = Nothing, peerType = Nothing, preferences = Nothing, badge = Nothing, contactDomain = Nothing}
pure $ Profile {displayName = adjective <> noun, fullName = "", shortDescr = Nothing, description = Nothing, image = Nothing, contactLink = Nothing, peerType, preferences = Nothing, badge = Nothing, contactDomain = Nothing}
where
pick :: [a] -> IO a
pick xs = (xs !!) <$> randomRIO (0, length xs - 1)
@@ -5108,8 +5108,8 @@ Query:
Plan:
Query:
INSERT INTO contact_profiles (display_name, full_name, short_descr, description, image, user_id, incognito, created_at, updated_at)
VALUES (?,?,?,?,?,?,?,?,?)
INSERT INTO contact_profiles (display_name, full_name, short_descr, description, image, chat_peer_type, user_id, incognito, created_at, updated_at)
VALUES (?,?,?,?,?,?,?,?,?,?)
Plan:
SEARCH contact_requests USING COVERING INDEX idx_contact_requests_contact_profile_id (contact_profile_id=?)
+4 -4
View File
@@ -323,14 +323,14 @@ createConnection_ db userId connType entityId acId connStatus connChatVersion pe
ent ct = if connType == ct then entityId else Nothing
createIncognitoProfile_ :: DB.Connection -> UserId -> UTCTime -> Profile -> IO Int64
createIncognitoProfile_ db userId createdAt Profile {displayName, fullName, shortDescr, description, image} = do
createIncognitoProfile_ db userId createdAt Profile {displayName, fullName, shortDescr, description, image, peerType} = do
DB.execute
db
[sql|
INSERT INTO contact_profiles (display_name, full_name, short_descr, description, image, user_id, incognito, created_at, updated_at)
VALUES (?,?,?,?,?,?,?,?,?)
INSERT INTO contact_profiles (display_name, full_name, short_descr, description, image, chat_peer_type, user_id, incognito, created_at, updated_at)
VALUES (?,?,?,?,?,?,?,?,?,?)
|]
(displayName, fullName, shortDescr, description, image, userId, Just (BI True), createdAt, createdAt)
(displayName, fullName, shortDescr, description, image, peerType, userId, Just (BI True), createdAt, createdAt)
insertedRowId db
updateConnSupportPQ :: DB.Connection -> Int64 -> PQSupport -> PQEncryption -> IO ()
+33 -1
View File
@@ -32,7 +32,7 @@ import Simplex.Chat.Options (ChatOpts (..), CoreChatOpts (..))
import Simplex.Chat.Protocol (ChatBinding (..), ChatMsgEvent (XInfo), ContactShortLinkData (ContactShortLinkData), LinkOwnerSig, MsgChatLink (..), MsgContent (..), encodeChatBinding)
import Simplex.Chat.Store.Groups (getGroupMembers)
import Simplex.Chat.Store.Shared (createContact, getGroupInfoKeys)
import Simplex.Chat.Types (AgentConnId (..), ConnStatus (..), GroupId, GroupInfo (..), GroupInfoKeys (..), GroupMember (..), Profile (..), GroupRejectionReason (..), fromLocalProfile, profileFromName, userProfileDirect, pattern VersionChat)
import Simplex.Chat.Types (AgentConnId (..), ChatPeerType (..), ConnStatus (..), GroupId, GroupInfo (..), GroupInfoKeys (..), GroupMember (..), Profile (..), GroupRejectionReason (..), fromLocalProfile, profileFromName, userProfileDirect, pattern VersionChat)
import qualified Simplex.Messaging.Crypto as C
import Simplex.Messaging.Crypto.BBS (BBSPublicKey, BBSSecretKey, bbsKeyGen)
import Simplex.Chat.Types.Shared (GroupMemberRole (..))
@@ -133,6 +133,8 @@ chatProfileTests = do
it "connection incognito unchanged errors" testConnectionIncognitoUnchangedErrors
it "set, reset, set connection incognito" testSetResetSetConnectionIncognito
it "join group incognito" testJoinGroupIncognito
it "join group incognito as bot" $ testJoinGroupIncognitoPeerType (Just CPTBot)
it "join group incognito as human" $ testJoinGroupIncognitoPeerType Nothing
it "can't invite contact to whom user connected incognito to a group" testCantInviteContactIncognito
it "can't see global preferences update" testCantSeeGlobalPrefsUpdateIncognito
it "deleting contact first, group second deletes incognito profile" testDeleteContactThenGroupDeletesIncognitoProfile
@@ -4905,6 +4907,36 @@ testShortLinkConnectPreparedGroupIncognito = testChat3 aliceProfile bobProfile c
alice <# "#team cath> 3"
bob ?<# "#team cath> 3"
testJoinGroupIncognitoPeerType :: HasCallStack => Maybe ChatPeerType -> TestParams -> IO ()
testJoinGroupIncognitoPeerType peerType_ = testChat3 aliceProfile bobProfile {peerType = peerType_} cathProfile test
where
test alice bob cath = do
createGroup2 "team" alice cath
alice ##> "/create link #team"
(shortLink, fullLink) <- getGroupLinks alice "team" GRMember True
bob ##> ("/_connect plan 1 " <> shortLink)
bob <## "group link: ok to connect directly"
groupSLinkData <- getTermLine bob
bob ##> ("/_prepare group 1 " <> fullLink <> " " <> shortLink <> " " <> groupSLinkData)
bob <## "#team: group is prepared"
bob ##> "/_connect group #1 incognito=on"
bobIncognito <- getTermLine bob
bob <## "#team: connection started incognito"
alice <## (bobIncognito <> ": accepting request to join group #team...")
concurrentlyN_
[ alice <## ("#team: " <> bobIncognito <> " joined the group"),
do
bob <## "#team: joining the group..."
bob <## ("#team: you joined the group incognito as " <> bobIncognito)
bob <## "#team: member cath (Catherine) is connected",
do
cath <## ("#team: alice added " <> bobIncognito <> " to the group (connecting...)")
cath <## ("#team: new member " <> bobIncognito <> " is connected")
]
forM_ [alice, cath] $ \cc -> do
GroupMember {memberProfile} <- getTestMember cc "team" (T.pack bobIncognito)
peerType (fromLocalProfile memberProfile) `shouldBe` peerType_
testShortLinkChangePreparedGroupUser :: HasCallStack => TestParams -> IO ()
testShortLinkChangePreparedGroupUser = testChat3 aliceProfile bobProfile cathProfile test
where