core: api to change user of pending connections (#4681)

* core: add api that enables change of owner user id for pending connections

* old user sends request, incognito handling and coverage

* call agent inside set connection api

* only set user id if servers match

* simplify

* reduce test noise

* return invitation when a newone is created

* add test for profile on different server

* refactor namings

* update simplexmq

* refactor

* test improvements and simplify

* remove fdescribes

* simplify and reduce vars scope

* put if back

* refactor, change error

* refactor view

* refactor

---------

Co-authored-by: spaced4ndy <8711996+spaced4ndy@users.noreply.github.com>
This commit is contained in:
Diogo
2024-08-21 10:27:58 +01:00
committed by GitHub
parent e04f74738e
commit d5eb7b7811
7 changed files with 195 additions and 2 deletions
+35
View File
@@ -1655,6 +1655,40 @@ processChatCommand' vr = \case
case conn'_ of
Just conn' -> pure $ CRConnectionIncognitoUpdated user conn'
Nothing -> throwChatError CEConnectionIncognitoChangeProhibited
APIChangeConnectionUser connId newUserId -> withUser $ \user@User {userId} -> do
conn <- withFastStore $ \db -> getPendingContactConnection db userId connId
let PendingContactConnection {pccConnStatus, connReqInv} = conn
case (pccConnStatus, connReqInv) of
(ConnNew, Just cReqInv) -> do
newUser <- privateGetUser newUserId
conn' <- ifM (canKeepLink cReqInv newUser) (updateConnRecord user conn newUser) (recreateConn user conn newUser)
pure $ CRConnectionUserChanged user conn conn' newUser
_ -> throwChatError CEConnectionUserChangeProhibited
where
canKeepLink :: ConnReqInvitation -> User -> CM Bool
canKeepLink (CRInvitationUri crData _) newUser = do
let ConnReqUriData {crSmpQueues = q :| _} = crData
SMPQueueUri {queueAddress = SMPQueueAddress {smpServer}} = q
cfg <- asks config
newUserServers <- L.map (\ServerCfg {server} -> protoServer server) . useServers cfg SPSMP <$> withFastStore' (`getProtocolServers` newUser)
pure $ smpServer `elem` newUserServers
updateConnRecord user@User {userId} conn@PendingContactConnection {customUserProfileId} newUser = do
withAgent $ \a -> changeConnectionUser a (aUserId user) (aConnId' conn) (aUserId newUser)
withFastStore' $ \db -> do
conn' <- updatePCCUser db userId conn newUserId
forM_ customUserProfileId $ \profileId ->
deletePCCIncognitoProfile db user profileId
pure conn'
recreateConn user conn@PendingContactConnection {customUserProfileId} newUser = do
subMode <- chatReadVar subscriptionMode
(agConnId, cReq) <- withAgent $ \a -> createConnection a (aUserId user) True SCMInvitation Nothing IKPQOn subMode
conn' <- withFastStore' $ \db -> do
deleteConnectionRecord db user connId
forM_ customUserProfileId $ \profileId ->
deletePCCIncognitoProfile db user profileId
createDirectConnection db newUser agConnId cReq ConnNew Nothing subMode initialChatVersion PQSupportOn
deleteAgentConnectionAsync user (aConnId' conn)
pure conn'
APIConnectPlan userId cReqUri -> withUserId userId $ \user ->
CRConnectionPlan user <$> connectPlan user cReqUri
APIConnect userId incognito (Just (ACR SCMInvitation cReq)) -> withUserId userId $ \user -> withInvitationLock "connect" (strEncode cReq) . procCmd $ do
@@ -7790,6 +7824,7 @@ chatCommandP =
"/_connect " *> (APIConnect <$> A.decimal <*> incognitoOnOffP <* A.space <*> ((Just <$> strP) <|> A.takeByteString $> Nothing)),
"/_connect " *> (APIAddContact <$> A.decimal <*> incognitoOnOffP),
"/_set incognito :" *> (APISetConnectionIncognito <$> A.decimal <* A.space <*> onOffP),
"/_set conn user :" *> (APIChangeConnectionUser <$> A.decimal <* A.space <*> A.decimal),
("/connect" <|> "/c") *> (Connect <$> incognitoP <* A.space <*> ((Just <$> strP) <|> A.takeTill isSpace $> Nothing)),
("/connect" <|> "/c") *> (AddContact <$> incognitoP),
ForwardMessage <$> chatNameP <* " <- @" <*> displayName <* A.space <*> msgTextP,
+3
View File
@@ -403,6 +403,7 @@ data ChatCommand
| APIAddContact UserId IncognitoEnabled
| AddContact IncognitoEnabled
| APISetConnectionIncognito Int64 IncognitoEnabled
| APIChangeConnectionUser Int64 UserId -- new user id to switch connection to
| APIConnectPlan UserId AConnectionRequestUri
| APIConnect UserId IncognitoEnabled (Maybe AConnectionRequestUri)
| Connect IncognitoEnabled (Maybe AConnectionRequestUri)
@@ -628,6 +629,7 @@ data ChatResponse
| CRVersionInfo {versionInfo :: CoreVersionInfo, chatMigrations :: [UpMigration], agentMigrations :: [UpMigration]}
| CRInvitation {user :: User, connReqInvitation :: ConnReqInvitation, connection :: PendingContactConnection}
| CRConnectionIncognitoUpdated {user :: User, toConnection :: PendingContactConnection}
| CRConnectionUserChanged {user :: User, fromConnection :: PendingContactConnection, toConnection :: PendingContactConnection, newUser :: User}
| CRConnectionPlan {user :: User, connectionPlan :: ConnectionPlan}
| CRSentConfirmation {user :: User, connection :: PendingContactConnection}
| CRSentInvitation {user :: User, connection :: PendingContactConnection, customUserProfile :: Maybe Profile}
@@ -1191,6 +1193,7 @@ data ChatErrorType
| CEAgentCommandError {message :: String}
| CEInvalidFileDescription {message :: String}
| CEConnectionIncognitoChangeProhibited
| CEConnectionUserChangeProhibited
| CEPeerChatVRangeIncompatible
| CEInternalError {message :: String}
| CEException {message :: String}
+14
View File
@@ -64,6 +64,7 @@ module Simplex.Chat.Store.Direct
createAcceptedContact,
getUserByContactRequestId,
getPendingContactConnections,
updatePCCUser,
getContactConnections,
getConnectionById,
getConnectionsContacts,
@@ -424,6 +425,19 @@ updatePCCIncognito db User {userId} conn customUserProfileId = do
(customUserProfileId, updatedAt, userId, pccConnId conn)
pure (conn :: PendingContactConnection) {customUserProfileId, updatedAt}
updatePCCUser :: DB.Connection -> UserId -> PendingContactConnection -> UserId -> IO PendingContactConnection
updatePCCUser db userId conn newUserId = do
updatedAt <- getCurrentTime
DB.execute
db
[sql|
UPDATE connections
SET user_id = ?, custom_user_profile_id = NULL, updated_at = ?
WHERE user_id = ? AND connection_id = ?
|]
(newUserId, updatedAt, userId, pccConnId conn)
pure (conn :: PendingContactConnection) {customUserProfileId = Nothing, updatedAt}
deletePCCIncognitoProfile :: DB.Connection -> User -> ProfileId -> IO ()
deletePCCIncognitoProfile db User {userId} profileId =
DB.execute
+16
View File
@@ -172,6 +172,7 @@ responseToView hu@(currentRH, user_) ChatConfig {logLevel, showReactions, showRe
CRVersionInfo info _ _ -> viewVersionInfo logLevel info
CRInvitation u cReq _ -> ttyUser u $ viewConnReqInvitation cReq
CRConnectionIncognitoUpdated u c -> ttyUser u $ viewConnectionIncognitoUpdated c
CRConnectionUserChanged u c c' nu -> ttyUser u $ viewConnectionUserChanged u c nu c'
CRConnectionPlan u connectionPlan -> ttyUser u $ viewConnectionPlan connectionPlan
CRSentConfirmation u _ -> ttyUser u ["confirmation sent!"]
CRSentInvitation u _ customUserProfile -> ttyUser u $ viewSentInvitation customUserProfile testView
@@ -1498,6 +1499,20 @@ viewConnectionIncognitoUpdated PendingContactConnection {pccConnId, customUserPr
| isJust customUserProfileId = ["connection " <> sShow pccConnId <> " changed to incognito"]
| otherwise = ["connection " <> sShow pccConnId <> " changed to non incognito"]
viewConnectionUserChanged :: User -> PendingContactConnection -> User -> PendingContactConnection -> [StyledString]
viewConnectionUserChanged User {localDisplayName = n} PendingContactConnection {pccConnId, connReqInv} User {localDisplayName = n'} PendingContactConnection {connReqInv = connReqInv'} =
case (connReqInv, connReqInv') of
(Just cReqInv, Just cReqInv')
| cReqInv /= cReqInv' -> [userChangedStr <> ", new link:"] <> newLink cReqInv'
_ -> [userChangedStr]
where
userChangedStr = "connection " <> sShow pccConnId <> " changed from user " <> plain n <> " to user " <> plain n'
newLink cReqInv =
[ "",
(plain . strEncode) (simplexChatInvitation cReqInv),
""
]
viewConnectionPlan :: ConnectionPlan -> [StyledString]
viewConnectionPlan = \case
CPInvitationLink ilp -> case ilp of
@@ -2025,6 +2040,7 @@ viewChatError isCmd logLevel testView = \case
CEAgentCommandError e -> ["agent command error: " <> plain e]
CEInvalidFileDescription e -> ["invalid file description: " <> plain e]
CEConnectionIncognitoChangeProhibited -> ["incognito mode change prohibited"]
CEConnectionUserChangeProhibited -> ["incognito mode change prohibited for user"]
CEPeerChatVRangeIncompatible -> ["peer chat protocol version range incompatible"]
CEInternalError e -> ["internal chat error: " <> plain e]
CEException e -> ["exception: " <> plain e]