Skip to content

Commit 07eaf91

Browse files
authored
smp server: allow getting and deleting short links for the old contact queues (#1549)
* smp server: allow getting and deleting short links for the old contact queues * fix verifaction of legacy contact queues * test
1 parent 56ea2fd commit 07eaf91

File tree

5 files changed

+85
-34
lines changed

5 files changed

+85
-34
lines changed

src/Simplex/Messaging/Server.hs

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1049,7 +1049,7 @@ verifyTransmission ms auth_ tAuth authorized queueId cmd =
10491049
Cmd SSender PING -> pure $ VRVerified Nothing
10501050
Cmd SSender RFWD {} -> pure $ VRVerified Nothing
10511051
Cmd SSenderLink (LKEY k) -> verifySecure SSenderLink k
1052-
Cmd SSenderLink LGET -> verifyQueue (\q -> if isContact (snd q) then VRVerified (Just q) else VRFailed) <$> get SSenderLink
1052+
Cmd SSenderLink LGET -> verifyQueue (\q -> if isContactQueue (snd q) then VRVerified (Just q) else VRFailed) <$> get SSenderLink
10531053
-- NSUB will not be accepted without authorization
10541054
Cmd SNotifier NSUB -> verifyQueue (\q -> maybe dummyVerify (\n -> Just q `verifiedWith` notifierKey n) (notifier $ snd q)) <$> get SNotifier
10551055
Cmd SProxiedClient _ -> pure $ VRVerified Nothing
@@ -1067,12 +1067,15 @@ verifyTransmission ms auth_ tAuth authorized queueId cmd =
10671067
allowedKey k = \case
10681068
QueueRec {queueMode = Just QMMessaging, senderKey} -> maybe True (k ==) senderKey
10691069
_ -> False
1070-
isContact = \case
1071-
QueueRec {queueMode = Just QMContact} -> True
1072-
_ -> False
10731070
get :: DirectParty p => SParty p -> M (Either ErrorType (StoreQueue s, QueueRec))
10741071
get party = liftIO $ getQueueRec ms party queueId
10751072

1073+
isContactQueue :: QueueRec -> Bool
1074+
isContactQueue QueueRec {queueMode, senderKey} = case queueMode of
1075+
Just QMMessaging -> False
1076+
Just QMContact -> True
1077+
Nothing -> isNothing senderKey -- for backward compatibility with pre-SKEY contact addresses
1078+
10761079
verifyCmdAuthorization :: Maybe (THandleAuth 'TServer, C.CbNonce) -> Maybe TransmissionAuth -> ByteString -> C.APublicAuthKey -> Bool
10771080
verifyCmdAuthorization auth_ tAuth authorized key = maybe False (verify key) tAuth
10781081
where
@@ -1231,7 +1234,7 @@ client
12311234
RFWD encBlock -> (corrId, NoEntity,) <$> processForwardedCommand encBlock
12321235
Cmd SSenderLink command -> Just <$> case command of
12331236
LKEY k -> withQueue $ \q qr -> checkMode QMMessaging qr $ secureQueue_ q k $>> getQueueLink_ q qr
1234-
LGET -> withQueue $ \q qr -> checkMode QMContact qr $ getQueueLink_ q qr
1237+
LGET -> withQueue $ \q qr -> checkContact qr $ getQueueLink_ q qr
12351238
Cmd SNotifier NSUB -> Just <$> subscribeNotifications
12361239
Cmd SRecipient command ->
12371240
Just <$> case command of
@@ -1247,17 +1250,11 @@ client
12471250
KEY sKey -> withQueue $ \q _ -> either err (corrId,entId,) <$> secureQueue_ q sKey
12481251
RKEY rKeys -> withQueue $ \q qr -> checkMode QMContact qr $ OK <$$ liftIO (updateKeys (queueStore ms) q rKeys)
12491252
LSET lnkId d ->
1250-
withQueue $ \q QueueRec {queueMode, senderKey, queueData} ->
1251-
liftIO $ either err (corrId,entId,)
1252-
-- this check allows adding link data to contact addresses created prior to SKEY,
1253-
-- using `queueMode == Just QMContact` would prevent it, they have queueMode `Nothing`.
1254-
<$> if queueMode /= Just QMMessaging && isNothing senderKey
1255-
then case queueData of
1256-
Just (lnkId', _) | lnkId' /= lnkId -> pure $ Left AUTH
1257-
_ -> OK <$$ addQueueLinkData (queueStore ms) q lnkId d
1258-
else pure $ Left AUTH
1253+
withQueue $ \q qr -> checkContact qr $ liftIO $ case queueData qr of
1254+
Just (lnkId', _) | lnkId' /= lnkId -> pure $ Left AUTH
1255+
_ -> OK <$$ addQueueLinkData (queueStore ms) q lnkId d
12591256
LDEL ->
1260-
withQueue $ \q qr -> checkMode QMContact qr $ liftIO $ case queueData qr of
1257+
withQueue $ \q qr -> checkContact qr $ liftIO $ case queueData qr of
12611258
Just _ -> OK <$$ deleteQueueLinkData (queueStore ms) q
12621259
Nothing -> pure $ Right OK
12631260
NKEY nKey dhKey -> withQueue $ \q _ -> addQueueNotifier_ q nKey dhKey
@@ -1327,6 +1324,13 @@ client
13271324
pure $ IDS QIK {rcvId, sndId, rcvPublicDhKey, queueMode, linkId = fst <$> queueData} -- , serverNtfCreds = snd <$> ntf
13281325
(corrId,entId,) <$> tryCreate (3 :: Int)
13291326

1327+
-- this check allows to support contact queues created prior to SKEY,
1328+
-- using `queueMode == Just QMContact` would prevent it, as they have queueMode `Nothing`.
1329+
checkContact :: QueueRec -> M (Either ErrorType BrokerMsg) -> M (Transmission BrokerMsg)
1330+
checkContact qr a =
1331+
either err (corrId,entId,)
1332+
<$> if isContactQueue qr then a else pure $ Left AUTH
1333+
13301334
checkMode :: QueueMode -> QueueRec -> M (Either ErrorType BrokerMsg) -> M (Transmission BrokerMsg)
13311335
checkMode qm QueueRec {queueMode} a =
13321336
either err (corrId,entId,)

src/Simplex/Messaging/Server/QueueStore/Postgres.hs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ module Simplex.Messaging.Server.QueueStore.Postgres
2525
foldQueueRecs,
2626
handleDuplicate,
2727
withLog_,
28+
withDB',
2829
)
2930
where
3031

@@ -138,7 +139,7 @@ instance StoreQueueClass q => QueueStoreClass q (PostgresQueueStore q) where
138139
db
139140
[sql|
140141
SELECT
141-
(SELECT COUNT(1) FROM msg_queues WHERE deleted_at IS NULL) AS queue_count,
142+
(SELECT COUNT(1) FROM msg_queues WHERE deleted_at IS NULL) AS queue_count,
142143
(SELECT COUNT(1) FROM msg_queues WHERE deleted_at IS NULL AND notifier_id IS NOT NULL) AS notifier_count
143144
|]
144145
pure QueueCounts {queueCount, notifierCount}
@@ -221,7 +222,7 @@ instance StoreQueueClass q => QueueStoreClass q (PostgresQueueStore q) where
221222
_ -> throwE AUTH
222223

223224
addQueueLinkData :: PostgresQueueStore q -> q -> LinkId -> QueueLinkData -> IO (Either ErrorType ())
224-
addQueueLinkData st sq lnkId d =
225+
addQueueLinkData st sq lnkId d =
225226
withQueueRec sq "addQueueLinkData" $ \q -> case queueData q of
226227
Nothing ->
227228
addLink q $ \db -> DB.execute db qry (d :. (lnkId, rId))
@@ -335,7 +336,7 @@ instance StoreQueueClass q => QueueStoreClass q (PostgresQueueStore q) where
335336
unblockQueue st sq =
336337
setStatusDB "unblockQueue" st sq EntityActive $
337338
withLog "unblockQueue" st (`logUnblockQueue` recipientId sq)
338-
339+
339340
updateQueueTime :: PostgresQueueStore q -> q -> RoundedSystemTime -> IO (Either ErrorType QueueRec)
340341
updateQueueTime st sq t =
341342
withQueueRec sq "updateQueueTime" $ \q@QueueRec {updatedAt} ->

tests/AgentTests/FunctionalAPITests.hs

Lines changed: 57 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,18 +71,20 @@ import Data.List.NonEmpty (NonEmpty)
7171
import qualified Data.Map as M
7272
import Data.Maybe (isJust, isNothing)
7373
import qualified Data.Set as S
74+
import qualified Data.Text as T
7475
import Data.Text.Encoding (decodeLatin1)
76+
import qualified Data.Text.IO as T
7577
import Data.Time.Clock (diffUTCTime, getCurrentTime)
7678
import Data.Time.Clock.System (SystemTime (..), getSystemTime)
7779
import Data.Type.Equality (testEquality, (:~:) (Refl))
7880
import Data.Word (Word16)
7981
import GHC.Stack (withFrozenCallStack)
8082
import SMPAgentClient
81-
import SMPClient (cfgJ2QS, cfgMS, prevRange, prevVersion, proxyCfgJ2QS, proxyCfgMS, testPort, testPort2, testStoreLogFile, withSmpServer, withSmpServers2, withSmpServerConfigOn, withSmpServerProxy, withSmpServersProxy2, withSmpServerStoreLogOn, withSmpServerStoreMsgLogOn)
83+
import SMPClient
8284
import Simplex.Messaging.Agent hiding (createConnection, joinConnection, sendMessage)
8385
import qualified Simplex.Messaging.Agent as A
8486
import Simplex.Messaging.Agent.Client (ProtocolTestFailure (..), ProtocolTestStep (..), ServerQueueInfo (..), UserNetworkInfo (..), UserNetworkType (..), waitForUserNetwork)
85-
import Simplex.Messaging.Agent.Env.SQLite (AgentConfig (..), InitialAgentServers (..), createAgentStore)
87+
import Simplex.Messaging.Agent.Env.SQLite (AgentConfig (..), Env (..), InitialAgentServers (..), createAgentStore)
8688
import Simplex.Messaging.Agent.Protocol hiding (CON, CONF, INFO, REQ, SENT)
8789
import qualified Simplex.Messaging.Agent.Protocol as A
8890
import Simplex.Messaging.Agent.Store.Common (DBStore (..), withTransaction)
@@ -115,6 +117,15 @@ import XFTPClient (testXFTPServer)
115117
#if defined(dbPostgres)
116118
import Fixtures
117119
#endif
120+
#if defined(dbServerPostgres)
121+
import qualified Database.PostgreSQL.Simple as PSQL
122+
import Simplex.Messaging.Agent.Store (Connection (..), StoredRcvQueue (..), SomeConn (..))
123+
import Simplex.Messaging.Agent.Store.AgentStore (getConn)
124+
import Simplex.Messaging.Server.MsgStore.Journal (JournalQueue)
125+
import Simplex.Messaging.Server.MsgStore.Types (QSType (..))
126+
import Simplex.Messaging.Server.QueueStore.Postgres
127+
import Simplex.Messaging.Server.QueueStore.Types (QueueStoreClass (..))
128+
#endif
118129

119130
type AEntityTransmission e = (ACorrId, ConnId, AEvent e)
120131

@@ -320,6 +331,7 @@ functionalAPITests ps = do
320331
it "should get 1-time link data after restart" $ testInviationShortLinkRestart ps
321332
it "should connect via contact short link after restart" $ testContactShortLinkRestart ps
322333
it "should connect via added contact short link after restart" $ testAddContactShortLinkRestart ps
334+
it "should create and get short links with the old contact queues" $ testOldContactQueueShortLink ps
323335
describe "Message delivery" $ do
324336
describe "update connection agent version on received messages" $ do
325337
it "should increase if compatible, shouldn'ps decrease" $
@@ -1307,6 +1319,49 @@ testAddContactShortLinkRestart ps = withAgentClients2 $ \a b -> do
13071319
connReq4 `shouldBe` connReq
13081320
linkUserData updatedConnData' `shouldBe` updatedData
13091321

1322+
testOldContactQueueShortLink :: HasCallStack => (ASrvTransport, AStoreType) -> IO ()
1323+
testOldContactQueueShortLink ps@(_, msType) = withAgentClients2 $ \a b -> do
1324+
(contactId, CCLink connReq Nothing) <- withSmpServer ps $ runRight $
1325+
A.createConnection a 1 True SCMContact Nothing Nothing CR.IKPQOn SMOnlyCreate
1326+
-- make it an "old" queue
1327+
let updateStoreLog f = replaceSubstringInFile f " queue_mode=C" ""
1328+
() <- case testServerStoreConfig msType of
1329+
ASSCfg _ _ (SSCMemory (Just StorePaths {storeLogFile})) -> updateStoreLog storeLogFile
1330+
ASSCfg _ _ (SSCMemoryJournal {storeLogFile}) -> updateStoreLog storeLogFile
1331+
ASSCfg _ _ (SSCDatabaseJournal {storeCfg}) -> do
1332+
#if defined(dbServerPostgres)
1333+
let AgentClient {agentEnv = Env {store}} = a
1334+
Right (SomeConn _ (ContactConnection _ RcvQueue {rcvId})) <- withTransaction store (`getConn` contactId)
1335+
st :: PostgresQueueStore (JournalQueue 'QSPostgres) <- newQueueStore @(JournalQueue 'QSPostgres) storeCfg
1336+
Right 1 <- runExceptT $ withDB' "test" st $ \db -> PSQL.execute db "UPDATE msg_queues SET queue_mode = ? WHERE recipient_id = ?" (Nothing :: Maybe QueueMode, rcvId)
1337+
closeQueueStore @(JournalQueue 'QSPostgres) st
1338+
#else
1339+
error "no dbServerPostgres flag"
1340+
#endif
1341+
_ -> pure ()
1342+
1343+
withSmpServer ps $ do
1344+
let userData = "some user data"
1345+
shortLink <- runRight $ setContactShortLink a contactId userData Nothing
1346+
(connReq', connData') <- runRight $ getConnShortLink b 1 shortLink
1347+
strDecode (strEncode shortLink) `shouldBe` Right shortLink
1348+
connReq' `shouldBe` connReq
1349+
linkUserData connData' `shouldBe` userData
1350+
-- update user data
1351+
let updatedData = "updated user data"
1352+
shortLink' <- runRight $ setContactShortLink a contactId updatedData Nothing
1353+
shortLink' `shouldBe` shortLink
1354+
-- check updated
1355+
(connReq'', updatedConnData') <- runRight $ getConnShortLink b 1 shortLink
1356+
connReq'' `shouldBe` connReq
1357+
linkUserData updatedConnData' `shouldBe` updatedData
1358+
1359+
replaceSubstringInFile :: FilePath -> T.Text -> T.Text -> IO ()
1360+
replaceSubstringInFile filePath oldText newText = do
1361+
content <- T.readFile filePath
1362+
let newContent = T.replace oldText newText content
1363+
T.writeFile filePath newContent
1364+
13101365
testIncreaseConnAgentVersion :: HasCallStack => (ASrvTransport, AStoreType) -> IO ()
13111366
testIncreaseConnAgentVersion ps = do
13121367
alice <- getSMPAgentClient' 1 agentCfg {smpAgentVRange = mkVersionRange 1 2} initAgentServers testDB

tests/AgentTests/NotificationTests.hs

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -53,15 +53,12 @@ import Data.ByteString.Char8 (ByteString)
5353
import qualified Data.ByteString.Char8 as B
5454
import Data.List.NonEmpty (NonEmpty (..))
5555
import qualified Data.List.NonEmpty as L
56-
import Data.Text (Text)
57-
import qualified Data.Text as T
5856
import Data.Text.Encoding (encodeUtf8)
59-
import qualified Data.Text.IO as TIO
6057
import Data.Time.Clock.System (systemToUTCTime)
6158
import qualified Database.PostgreSQL.Simple as PSQL
6259
import NtfClient
6360
import SMPAgentClient (agentCfg, initAgentServers, initAgentServers2, testDB, testDB2, testNtfServer, testNtfServer2)
64-
import SMPClient (cfgJ2QS, cfgMS, cfgVPrev, ntfTestPort, ntfTestPort2, serverStoreConfig, testPort, testPort2, withSmpServer, withSmpServerConfigOn, withSmpServerStoreLogOn, withSmpServerStoreMsgLogOn)
61+
import SMPClient (cfgJ2QS, cfgMS, cfgVPrev, ntfTestPort, ntfTestPort2, testServerStoreConfig, testPort, testPort2, withSmpServer, withSmpServerConfigOn, withSmpServerStoreLogOn, withSmpServerStoreMsgLogOn)
6562
import Simplex.Messaging.Agent hiding (createConnection, joinConnection, sendMessage)
6663
import Simplex.Messaging.Agent.Client (ProtocolTestFailure (..), ProtocolTestStep (..), withStore')
6764
import Simplex.Messaging.Agent.Env.SQLite (AgentConfig, Env (..), InitialAgentServers)
@@ -195,7 +192,7 @@ testNtfMatrix ps@(_, msType) runTest = do
195192

196193
runNtfTestCfg :: HasCallStack => (ASrvTransport, AStoreType) -> AgentMsgId -> ServerConfig -> NtfServerConfig -> AgentConfig -> AgentConfig -> (APNSMockServer -> AgentMsgId -> AgentClient -> AgentClient -> IO ()) -> IO ()
197194
runNtfTestCfg (t, msType) baseId smpCfg ntfCfg aCfg bCfg runTest = do
198-
let smpCfg' = smpCfg {serverStoreCfg = serverStoreConfig msType}
195+
let smpCfg' = smpCfg {serverStoreCfg = testServerStoreConfig msType}
199196
withSmpServerConfigOn t smpCfg' testPort $ \_ ->
200197
withAPNSMockServer $ \apns ->
201198
withNtfServerCfg ntfCfg {transports = [(ntfTestPort, t, False)]} $ \_ ->
@@ -498,12 +495,6 @@ testNtfTokenReRegisterInvalid t apns = do
498495
NTActive <- checkNtfToken a tkn1
499496
pure ()
500497

501-
replaceSubstringInFile :: FilePath -> Text -> Text -> IO ()
502-
replaceSubstringInFile filePath oldText newText = do
503-
content <- TIO.readFile filePath
504-
let newContent = T.replace oldText newText content
505-
TIO.writeFile filePath newContent
506-
507498
testNtfTokenReRegisterInvalidOnCheck :: ASrvTransport -> APNSMockServer -> IO ()
508499
testNtfTokenReRegisterInvalidOnCheck t apns = do
509500
tkn <- withNtfServer t $ do

tests/SMPClient.hs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -190,7 +190,7 @@ cfgMS msType =
190190
maxJournalStateLines = 2,
191191
queueIdBytes = 24,
192192
msgIdBytes = 24,
193-
serverStoreCfg = serverStoreConfig msType,
193+
serverStoreCfg = testServerStoreConfig msType,
194194
storeNtfsFile = Nothing,
195195
allowNewQueues = True,
196196
newQueueBasicAuth = Nothing,
@@ -229,8 +229,8 @@ cfgMS msType =
229229
defaultStartOptions :: StartOptions
230230
defaultStartOptions = StartOptions {maintenance = False, compactLog = False, logLevel = testLogLevel, skipWarnings = False, confirmMigrations = MCYesUp}
231231

232-
serverStoreConfig :: AStoreType -> AServerStoreCfg
233-
serverStoreConfig = serverStoreConfig_ False
232+
testServerStoreConfig :: AStoreType -> AServerStoreCfg
233+
testServerStoreConfig = serverStoreConfig_ False
234234

235235
serverStoreConfig_ :: Bool -> AStoreType -> AServerStoreCfg
236236
serverStoreConfig_ useDbStoreLog = \case

0 commit comments

Comments
 (0)