Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

core: check saved links and hashes by both connection request uri schemas for connection plan #3233

Merged
merged 1 commit into from
Oct 16, 2023
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
31 changes: 23 additions & 8 deletions src/Simplex/Chat.hs
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@
. map (\ServerCfg {server} -> server)
. filter (\ServerCfg {enabled} -> enabled)

cfgServers :: UserProtocol p => SProtocolType p -> (DefaultAgentServers -> NonEmpty (ProtoServerWithAuth p))

Check warning on line 276 in src/Simplex/Chat.hs

View workflow job for this annotation

GitHub Actions / build-ubuntu-20.04

Redundant constraint: UserProtocol p

Check warning on line 276 in src/Simplex/Chat.hs

View workflow job for this annotation

GitHub Actions / build-ubuntu-22.04

Redundant constraint: UserProtocol p

Check warning on line 276 in src/Simplex/Chat.hs

View workflow job for this annotation

GitHub Actions / build-macos-latest

Redundant constraint: UserProtocol p

Check warning on line 276 in src/Simplex/Chat.hs

View workflow job for this annotation

GitHub Actions / build-windows-latest

Redundant constraint: UserProtocol p
cfgServers p s = case p of
SPSMP -> s.smp
SPXFTP -> s.xftp
Expand Down Expand Up @@ -2222,7 +2222,7 @@
processChatCommand $ APISetChatSettings (ChatRef cType chatId) $ updateSettings chatSettings
connectPlan :: User -> AConnectionRequestUri -> m ConnectionPlan
connectPlan user (ACR SCMInvitation cReq) = do
withStore' (\db -> getConnectionEntityByConnReq db user cReq) >>= \case
withStore' (\db -> getConnectionEntityByConnReq db user cReqSchemas) >>= \case
Nothing -> pure $ CPInvitationLink ILPOk
Just (RcvDirectMsgConnection conn ct_) -> do
let Connection {connStatus, contactConnInitiated} = conn
Expand All @@ -2235,17 +2235,23 @@
Just ct -> pure $ CPInvitationLink (ILPKnown ct)
Nothing -> throwChatError $ CEInternalError "ready RcvDirectMsgConnection connection should have associated contact"
Just _ -> throwChatError $ CECommandError "found connection entity is not RcvDirectMsgConnection"
where
cReqSchemas :: (ConnReqInvitation, ConnReqInvitation)
cReqSchemas = case cReq of
(CRInvitationUri crData e2e) ->
( CRInvitationUri crData {crScheme = CRSSimplex} e2e,
CRInvitationUri crData {crScheme = simplexChat} e2e
)
connectPlan user (ACR SCMContact cReq) = do
let CRContactUri ConnReqUriData {crClientData} = cReq
groupLinkId = crClientData >>= decodeJSON >>= \(CRDataGroup gli) -> Just gli
case groupLinkId of
-- contact address
Nothing ->
withStore' (`getUserContactLinkByConnReq` cReq) >>= \case
withStore' (`getUserContactLinkByConnReq` cReqSchemas) >>= \case
Just _ -> pure $ CPContactAddress CAPOwnLink
Nothing -> do
let cReqHash = ConnReqUriHash . C.sha256Hash $ strEncode cReq
withStore' (\db -> getContactConnEntityByConnReqHash db user cReqHash) >>= \case
withStore' (\db -> getContactConnEntityByConnReqHash db user cReqHashes) >>= \case
Nothing -> pure $ CPContactAddress CAPOk
Just (RcvDirectMsgConnection _conn Nothing) -> pure $ CPContactAddress CAPConnectingConfirmReconnect
Just (RcvDirectMsgConnection _ (Just ct))
Expand All @@ -2255,12 +2261,11 @@
Just _ -> throwChatError $ CECommandError "found connection entity is not RcvDirectMsgConnection"
-- group link
Just _ ->
withStore' (\db -> getGroupInfoByUserContactLinkConnReq db user cReq) >>= \case
withStore' (\db -> getGroupInfoByUserContactLinkConnReq db user cReqSchemas) >>= \case
Just g -> pure $ CPGroupLink (GLPOwnLink g)
Nothing -> do
let cReqHash = ConnReqUriHash . C.sha256Hash $ strEncode cReq
connEnt_ <- withStore' $ \db -> getContactConnEntityByConnReqHash db user cReqHash
gInfo_ <- withStore' $ \db -> getGroupInfoByGroupLinkHash db user cReqHash
connEnt_ <- withStore' $ \db -> getContactConnEntityByConnReqHash db user cReqHashes
gInfo_ <- withStore' $ \db -> getGroupInfoByGroupLinkHash db user cReqHashes
case (gInfo_, connEnt_) of
(Nothing, Nothing) -> pure $ CPGroupLink GLPOk
(Nothing, Just (RcvDirectMsgConnection _conn Nothing)) -> pure $ CPGroupLink GLPConnectingConfirmReconnect
Expand All @@ -2273,6 +2278,16 @@
pure $ CPGroupLink (GLPConnectingProhibit gInfo_)
| memberActive membership -> pure $ CPGroupLink (GLPKnown gInfo)
| otherwise -> pure $ CPGroupLink GLPOk
where
cReqSchemas :: (ConnReqContact, ConnReqContact)
cReqSchemas = case cReq of
(CRContactUri crData) ->
( CRContactUri crData {crScheme = CRSSimplex},
CRContactUri crData {crScheme = simplexChat}
)
cReqHashes :: (ConnReqUriHash, ConnReqUriHash)
cReqHashes = bimap hash hash cReqSchemas
hash = ConnReqUriHash . C.sha256Hash . strEncode

assertDirectAllowed :: ChatMonad m => User -> MsgDirection -> Contact -> CMEventTag e -> m ()
assertDirectAllowed user dir ct event =
Expand Down
14 changes: 7 additions & 7 deletions src/Simplex/Chat/Store/Connections.hs
Original file line number Diff line number Diff line change
Expand Up @@ -154,18 +154,18 @@ getConnectionEntity db user@User {userId, userContactId} agentConnId = do
userContact_ [(cReq, groupId)] = Right UserContact {userContactLinkId, connReqContact = cReq, groupId}
userContact_ _ = Left SEUserContactLinkNotFound

getConnectionEntityByConnReq :: DB.Connection -> User -> ConnReqInvitation -> IO (Maybe ConnectionEntity)
getConnectionEntityByConnReq db user cReq = do
getConnectionEntityByConnReq :: DB.Connection -> User -> (ConnReqInvitation, ConnReqInvitation) -> IO (Maybe ConnectionEntity)
getConnectionEntityByConnReq db user (cReqSchema1, cReqSchema2) = do
connId_ <- maybeFirstRow fromOnly $
DB.query db "SELECT agent_conn_id FROM connections WHERE conn_req_inv = ? LIMIT 1" (Only cReq)
DB.query db "SELECT agent_conn_id FROM connections WHERE conn_req_inv IN (?,?) LIMIT 1" (cReqSchema1, cReqSchema2)
maybe (pure Nothing) (fmap eitherToMaybe . runExceptT . getConnectionEntity db user) connId_

-- search connection for connection plan:
-- multiple connections can have same via_contact_uri_hash if request was repeated;
-- this function searches for latest connection with contact so that "known contact" plan would be chosen;
-- deleted connections are filtered out to allow re-connecting via same contact address
getContactConnEntityByConnReqHash :: DB.Connection -> User -> ConnReqUriHash -> IO (Maybe ConnectionEntity)
getContactConnEntityByConnReqHash db user cReqHash = do
getContactConnEntityByConnReqHash :: DB.Connection -> User -> (ConnReqUriHash, ConnReqUriHash) -> IO (Maybe ConnectionEntity)
getContactConnEntityByConnReqHash db user (cReqHash1, cReqHash2) = do
connId_ <- maybeFirstRow fromOnly $
DB.query
db
Expand All @@ -175,12 +175,12 @@ getContactConnEntityByConnReqHash db user cReqHash = do
agent_conn_id,
(CASE WHEN contact_id IS NOT NULL THEN 1 ELSE 0 END) AS conn_ord
FROM connections
WHERE via_contact_uri_hash = ? AND conn_status != ?
WHERE via_contact_uri_hash IN (?,?) AND conn_status != ?
ORDER BY conn_ord DESC, created_at DESC
LIMIT 1
)
|]
(cReqHash, ConnDeleted)
(cReqHash1, cReqHash2, ConnDeleted)
maybe (pure Nothing) (fmap eitherToMaybe . runExceptT . getConnectionEntity db user) connId_

getConnectionsToSubscribe :: DB.Connection -> IO ([ConnId], [ConnectionEntity])
Expand Down
16 changes: 8 additions & 8 deletions src/Simplex/Chat/Store/Groups.hs
Original file line number Diff line number Diff line change
Expand Up @@ -1121,33 +1121,33 @@ getGroupInfo db User {userId, userContactId} groupId =
|]
(groupId, userId, userContactId)

getGroupInfoByUserContactLinkConnReq :: DB.Connection -> User -> ConnReqContact -> IO (Maybe GroupInfo)
getGroupInfoByUserContactLinkConnReq db user cReq = do
getGroupInfoByUserContactLinkConnReq :: DB.Connection -> User -> (ConnReqContact, ConnReqContact) -> IO (Maybe GroupInfo)
getGroupInfoByUserContactLinkConnReq db user (cReqSchema1, cReqSchema2) = do
groupId_ <- maybeFirstRow fromOnly $
DB.query
db
[sql|
SELECT group_id
FROM user_contact_links
WHERE conn_req_contact = ?
WHERE conn_req_contact IN (?,?)
|]
(Only cReq)
(cReqSchema1, cReqSchema2)
maybe (pure Nothing) (fmap eitherToMaybe . runExceptT . getGroupInfo db user) groupId_

getGroupInfoByGroupLinkHash :: DB.Connection -> User -> ConnReqUriHash -> IO (Maybe GroupInfo)
getGroupInfoByGroupLinkHash db user@User {userId, userContactId} groupLinkHash = do
getGroupInfoByGroupLinkHash :: DB.Connection -> User -> (ConnReqUriHash, ConnReqUriHash) -> IO (Maybe GroupInfo)
getGroupInfoByGroupLinkHash db user@User {userId, userContactId} (groupLinkHash1, groupLinkHash2) = do
groupId_ <- maybeFirstRow fromOnly $
DB.query
db
[sql|
SELECT g.group_id
FROM groups g
JOIN group_members mu ON mu.group_id = g.group_id
WHERE g.user_id = ? AND g.via_group_link_uri_hash = ?
WHERE g.user_id = ? AND g.via_group_link_uri_hash IN (?,?)
AND mu.contact_id = ? AND mu.member_status NOT IN (?,?,?)
LIMIT 1
|]
(userId, groupLinkHash, userContactId, GSMemRemoved, GSMemLeft, GSMemGroupDeleted)
(userId, groupLinkHash1, groupLinkHash2, userContactId, GSMemRemoved, GSMemLeft, GSMemGroupDeleted)
maybe (pure Nothing) (fmap eitherToMaybe . runExceptT . getGroupInfo db user) groupId_

getGroupIdByName :: DB.Connection -> User -> GroupName -> ExceptT StoreError IO GroupId
Expand Down
8 changes: 4 additions & 4 deletions src/Simplex/Chat/Store/Profiles.hs
Original file line number Diff line number Diff line change
Expand Up @@ -441,17 +441,17 @@ getUserContactLinkById db userId userContactLinkId =
|]
(userId, userContactLinkId)

getUserContactLinkByConnReq :: DB.Connection -> ConnReqContact -> IO (Maybe UserContactLink)
getUserContactLinkByConnReq db cReq =
getUserContactLinkByConnReq :: DB.Connection -> (ConnReqContact, ConnReqContact) -> IO (Maybe UserContactLink)
getUserContactLinkByConnReq db (cReqSchema1, cReqSchema2) =
maybeFirstRow toUserContactLink $
DB.query
db
[sql|
SELECT conn_req_contact, auto_accept, auto_accept_incognito, auto_reply_msg_content
FROM user_contact_links
WHERE conn_req_contact = ?
WHERE conn_req_contact IN (?,?)
|]
(Only cReq)
(cReqSchema1, cReqSchema2)

updateUserAddressAutoAccept :: DB.Connection -> User -> Maybe AutoAccept -> ExceptT StoreError IO UserContactLink
updateUserAddressAutoAccept db user@User {userId} autoAccept = do
Expand Down
8 changes: 8 additions & 0 deletions tests/ChatTests/Direct.hs
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,10 @@ testPlanInvitationLinkOwn tmp =
alice ##> ("/_connect plan 1 " <> inv)
alice <## "invitation link: own link"

let invSchema2 = linkAnotherSchema inv
alice ##> ("/_connect plan 1 " <> invSchema2)
alice <## "invitation link: own link"

alice ##> ("/c " <> inv)
alice <## "confirmation sent!"
alice
Expand Down Expand Up @@ -305,6 +309,10 @@ testPlanInvitationLinkConnecting tmp = do
bob ##> ("/_connect plan 1 " <> inv)
bob <## "invitation link: connecting"

let invSchema2 = linkAnotherSchema inv
bob ##> ("/_connect plan 1 " <> invSchema2)
bob <## "invitation link: connecting"

testContactClear :: HasCallStack => FilePath -> IO ()
testContactClear =
testChat2 aliceProfile bobProfile $
Expand Down
33 changes: 33 additions & 0 deletions tests/ChatTests/Groups.hs
Original file line number Diff line number Diff line change
Expand Up @@ -2290,6 +2290,11 @@ testPlanGroupLinkOkKnown =
bob <## "group link: known group #team"
bob <## "use #team <message> to send messages"

let gLinkSchema2 = linkAnotherSchema gLink
bob ##> ("/_connect plan 1 " <> gLinkSchema2)
bob <## "group link: known group #team"
bob <## "use #team <message> to send messages"

bob ##> ("/c " <> gLink)
bob <## "group link: known group #team"
bob <## "use #team <message> to send messages"
Expand Down Expand Up @@ -2331,6 +2336,11 @@ testPlanHostContactDeletedGroupLinkKnown =
bob <## "group link: known group #team"
bob <## "use #team <message> to send messages"

let gLinkSchema2 = linkAnotherSchema gLink
bob ##> ("/_connect plan 1 " <> gLinkSchema2)
bob <## "group link: known group #team"
bob <## "use #team <message> to send messages"

bob ##> ("/c " <> gLink)
bob <## "group link: known group #team"
bob <## "use #team <message> to send messages"
Expand All @@ -2347,6 +2357,10 @@ testPlanGroupLinkOwn tmp =
alice ##> ("/_connect plan 1 " <> gLink)
alice <## "group link: own link for group #team"

let gLinkSchema2 = linkAnotherSchema gLink
alice ##> ("/_connect plan 1 " <> gLinkSchema2)
alice <## "group link: own link for group #team"

alice ##> ("/c " <> gLink)
alice <## "connection request sent!"
alice <## "alice_1 (Alice): accepting request to join group #team..."
Expand All @@ -2373,6 +2387,9 @@ testPlanGroupLinkOwn tmp =
alice ##> ("/_connect plan 1 " <> gLink)
alice <## "group link: own link for group #team"

alice ##> ("/_connect plan 1 " <> gLinkSchema2)
alice <## "group link: own link for group #team"

-- group works if merged contact is deleted
alice ##> "/d alice_1"
alice <## "alice_1: contact is deleted"
Expand Down Expand Up @@ -2405,6 +2422,10 @@ testPlanGroupLinkConnecting tmp = do
bob ##> ("/_connect plan 1 " <> gLink)
bob <## "group link: connecting, allowed to reconnect"

let gLinkSchema2 = linkAnotherSchema gLink
bob ##> ("/_connect plan 1 " <> gLinkSchema2)
bob <## "group link: connecting, allowed to reconnect"

threadDelay 100000
withTestChat tmp "alice" $ \alice -> do
alice
Expand All @@ -2417,6 +2438,10 @@ testPlanGroupLinkConnecting tmp = do
bob ##> ("/_connect plan 1 " <> gLink)
bob <## "group link: connecting"

let gLinkSchema2 = linkAnotherSchema gLink
bob ##> ("/_connect plan 1 " <> gLinkSchema2)
bob <## "group link: connecting"

bob ##> ("/c " <> gLink)
bob <## "group link: connecting"

Expand Down Expand Up @@ -2462,6 +2487,10 @@ testPlanGroupLinkLeaveRejoin =
bob ##> ("/_connect plan 1 " <> gLink)
bob <## "group link: ok to connect"

let gLinkSchema2 = linkAnotherSchema gLink
bob ##> ("/_connect plan 1 " <> gLinkSchema2)
bob <## "group link: ok to connect"

bob ##> ("/c " <> gLink)
bob <## "connection request sent!"
alice <## "bob_1 (Bob): accepting request to join group #team..."
Expand Down Expand Up @@ -2490,6 +2519,10 @@ testPlanGroupLinkLeaveRejoin =
bob <## "group link: known group #team_1"
bob <## "use #team_1 <message> to send messages"

bob ##> ("/_connect plan 1 " <> gLinkSchema2)
bob <## "group link: known group #team_1"
bob <## "use #team_1 <message> to send messages"

bob ##> ("/c " <> gLink)
bob <## "group link: known group #team_1"
bob <## "use #team_1 <message> to send messages"
Expand Down
28 changes: 27 additions & 1 deletion tests/ChatTests/Profiles.hs
Original file line number Diff line number Diff line change
Expand Up @@ -599,6 +599,11 @@
bob <## "contact address: known contact alice"
bob <## "use @alice <message> to send messages"

let cLinkSchema2 = linkAnotherSchema cLink
bob ##> ("/_connect plan 1 " <> cLinkSchema2)
bob <## "contact address: known contact alice"
bob <## "use @alice <message> to send messages"

bob ##> ("/c " <> cLink)
bob <## "contact address: known contact alice"
bob <## "use @alice <message> to send messages"
Expand All @@ -612,11 +617,15 @@
alice ##> ("/_connect plan 1 " <> cLink)
alice <## "contact address: own address"

let cLinkSchema2 = linkAnotherSchema cLink
alice ##> ("/_connect plan 1 " <> cLinkSchema2)
alice <## "contact address: own address"

alice ##> ("/c " <> cLink)
alice <## "connection request sent!"
alice <## "alice_1 (Alice) wants to connect to you!"
alice <## "to accept: /ac alice_1"
alice <## ("to reject: /rc alice_1 (the sender will NOT be notified)")
alice <## "to reject: /rc alice_1 (the sender will NOT be notified)"
alice @@@ [("<@alice_1", ""), (":2","")]
alice ##> "/ac alice_1"
alice <## "alice_1 (Alice): accepting contact request..."
Expand Down Expand Up @@ -658,6 +667,10 @@
bob ##> ("/_connect plan 1 " <> cLink)
bob <## "contact address: connecting, allowed to reconnect"

let cLinkSchema2 = linkAnotherSchema cLink
bob ##> ("/_connect plan 1 " <> cLinkSchema2)
bob <## "contact address: connecting, allowed to reconnect"

threadDelay 100000
withTestChat tmp "alice" $ \alice -> do
alice <## "Your address is active! To show: /sa"
Expand All @@ -672,6 +685,10 @@
bob ##> ("/_connect plan 1 " <> cLink)
bob <## "contact address: connecting to contact alice"

let cLinkSchema2 = linkAnotherSchema cLink
bob ##> ("/_connect plan 1 " <> cLinkSchema2)
bob <## "contact address: connecting to contact alice"

bob ##> ("/c " <> cLink)
bob <## "contact address: connecting to contact alice"

Expand Down Expand Up @@ -706,6 +723,10 @@
bob ##> ("/_connect plan 1 " <> cLink)
bob <## "contact address: ok to connect"

let cLinkSchema2 = linkAnotherSchema cLink
bob ##> ("/_connect plan 1 " <> cLinkSchema2)
bob <## "contact address: ok to connect"

bob ##> ("/c " <> cLink)
bob <## "connection request sent!"
alice <## "bob (Bob) wants to connect to you!"
Expand All @@ -726,6 +747,11 @@
bob <## "contact address: known contact alice_1"
bob <## "use @alice_1 <message> to send messages"

let cLinkSchema2 = linkAnotherSchema cLink

Check warning on line 750 in tests/ChatTests/Profiles.hs

View workflow job for this annotation

GitHub Actions / build-ubuntu-20.04

This binding for ‘cLinkSchema2’ shadows the existing binding

Check warning on line 750 in tests/ChatTests/Profiles.hs

View workflow job for this annotation

GitHub Actions / build-ubuntu-22.04

This binding for ‘cLinkSchema2’ shadows the existing binding

Check warning on line 750 in tests/ChatTests/Profiles.hs

View workflow job for this annotation

GitHub Actions / build-macos-latest

This binding for ‘cLinkSchema2’ shadows the existing binding

Check warning on line 750 in tests/ChatTests/Profiles.hs

View workflow job for this annotation

GitHub Actions / build-windows-latest

This binding for ‘cLinkSchema2’ shadows the existing binding
bob ##> ("/_connect plan 1 " <> cLinkSchema2)
bob <## "contact address: known contact alice_1"
bob <## "use @alice_1 <message> to send messages"

bob ##> ("/c " <> cLink)
bob <## "contact address: known contact alice_1"
bob <## "use @alice_1 <message> to send messages"
Expand Down
8 changes: 8 additions & 0 deletions tests/ChatTests/Utils.hs
Original file line number Diff line number Diff line change
Expand Up @@ -559,3 +559,11 @@ currentChatVRangeInfo =

vRangeStr :: VersionRange -> String
vRangeStr (VersionRange minVer maxVer) = "(" <> show minVer <> ", " <> show maxVer <> ")"

linkAnotherSchema :: String -> String
linkAnotherSchema link
| "https://simplex.chat/" `isPrefixOf` link =
T.unpack $ T.replace "https://simplex.chat/" "simplex:/" $ T.pack link
| "simplex:/" `isPrefixOf` link =
T.unpack $ T.replace "simplex:/" "https://simplex.chat/" $ T.pack link
| otherwise = error "link starts with neither https://simplex.chat/ nor simplex:/"
Loading