From e14ab0fed0a9d40090ca248288bbbcc73a9f4831 Mon Sep 17 00:00:00 2001 From: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com> Date: Mon, 14 Nov 2022 08:04:11 +0000 Subject: [PATCH] core: support SMP basic auth / server password (#1358) --- cabal.project | 2 +- scripts/nix/sha256map.nix | 2 +- simplex-chat.cabal | 1 + src/Simplex/Chat.hs | 2 +- src/Simplex/Chat/Controller.hs | 4 +-- .../Migrations/M20221112_server_password.hs | 12 +++++++++ src/Simplex/Chat/Migrations/chat_schema.sql | 1 + src/Simplex/Chat/Options.hs | 8 +++--- src/Simplex/Chat/Store.hs | 27 ++++++++++--------- src/Simplex/Chat/View.hs | 4 +-- stack.yaml | 2 +- tests/ChatClient.hs | 3 ++- tests/ChatTests.hs | 2 ++ 13 files changed, 45 insertions(+), 25 deletions(-) create mode 100644 src/Simplex/Chat/Migrations/M20221112_server_password.hs diff --git a/cabal.project b/cabal.project index d2855a8ea0..6afa53f021 100644 --- a/cabal.project +++ b/cabal.project @@ -7,7 +7,7 @@ constraints: zip +disable-bzip2 +disable-zstd source-repository-package type: git location: https://github.com/simplex-chat/simplexmq.git - tag: d2b88a1baa390ec64b6535e32ce69f26f53f4d7a + tag: 95db734b2d89bdf35e413f0abd4eac4ed3c64fc3 source-repository-package type: git diff --git a/scripts/nix/sha256map.nix b/scripts/nix/sha256map.nix index 7a65c63a45..bede7681b5 100644 --- a/scripts/nix/sha256map.nix +++ b/scripts/nix/sha256map.nix @@ -1,5 +1,5 @@ { - "https://github.com/simplex-chat/simplexmq.git"."d2b88a1baa390ec64b6535e32ce69f26f53f4d7a" = "1ijmhi9srkyq43aflsgx38hfir3q3q5d9xlq13g1sdh43i4wmyvk"; + "https://github.com/simplex-chat/simplexmq.git"."95db734b2d89bdf35e413f0abd4eac4ed3c64fc3" = "139ixj08y2x4vyf9qj1d9sz35hqxpaiyy9wi5skkncxgzgcm1ksa"; "https://github.com/simplex-chat/direct-sqlcipher.git"."34309410eb2069b029b8fc1872deb1e0db123294" = "0kwkmhyfsn2lixdlgl15smgr1h5gjk7fky6abzh8rng2h5ymnffd"; "https://github.com/simplex-chat/sqlcipher-simple.git"."5e154a2aeccc33ead6c243ec07195ab673137221" = "1d1gc5wax4vqg0801ajsmx1sbwvd9y7p7b8mmskvqsmpbwgbh0m0"; "https://github.com/simplex-chat/aeson.git"."3eb66f9a68f103b5f1489382aad89f5712a64db7" = "0kilkx59fl6c3qy3kjczqvm8c3f4n3p0bdk9biyflf51ljnzp4yp"; diff --git a/simplex-chat.cabal b/simplex-chat.cabal index 04d1526293..acf8ab9ede 100644 --- a/simplex-chat.cabal +++ b/simplex-chat.cabal @@ -61,6 +61,7 @@ library Simplex.Chat.Migrations.M20221024_contact_used Simplex.Chat.Migrations.M20221025_chat_settings Simplex.Chat.Migrations.M20221029_group_link_id + Simplex.Chat.Migrations.M20221112_server_password Simplex.Chat.Mobile Simplex.Chat.Options Simplex.Chat.ProfileGenerator diff --git a/src/Simplex/Chat.hs b/src/Simplex/Chat.hs index 7f5a855ca8..6ddc135152 100644 --- a/src/Simplex/Chat.hs +++ b/src/Simplex/Chat.hs @@ -105,7 +105,7 @@ defaultChatConfig = testView = False } -_defaultSMPServers :: NonEmpty SMPServer +_defaultSMPServers :: NonEmpty SMPServerWithAuth _defaultSMPServers = L.fromList [ "smp://0YuTwO05YJWS8rkjn9eLJDjQhFKvIYd8d4xG8X1blIU=@smp8.simplex.im,beccx4yfxxbvyhqypaavemqurytl6hozr47wfc7uuecacjqdvwpw2xid.onion", diff --git a/src/Simplex/Chat/Controller.hs b/src/Simplex/Chat/Controller.hs index 2599e8f0c3..87458782fb 100644 --- a/src/Simplex/Chat/Controller.hs +++ b/src/Simplex/Chat/Controller.hs @@ -186,7 +186,7 @@ data ChatCommand | APIDeleteGroupLink GroupId | APIGetGroupLink GroupId | GetUserSMPServers - | SetUserSMPServers [SMPServer] + | SetUserSMPServers [SMPServerWithAuth] | APISetChatItemTTL (Maybe Int64) | APIGetChatItemTTL | APISetNetworkConfig NetworkConfig @@ -261,7 +261,7 @@ data ChatResponse | CRApiChat {chat :: AChat} | CRLastMessages {chatItems :: [AChatItem]} | CRApiParsedMarkdown {formattedText :: Maybe MarkdownList} - | CRUserSMPServers {smpServers :: [SMPServer]} + | CRUserSMPServers {smpServers :: [SMPServerWithAuth]} | CRChatItemTTL {chatItemTTL :: Maybe Int64} | CRNetworkConfig {networkConfig :: NetworkConfig} | CRContactInfo {contact :: Contact, connectionStats :: ConnectionStats, customUserProfile :: Maybe Profile} diff --git a/src/Simplex/Chat/Migrations/M20221112_server_password.hs b/src/Simplex/Chat/Migrations/M20221112_server_password.hs new file mode 100644 index 0000000000..ee8d0e470d --- /dev/null +++ b/src/Simplex/Chat/Migrations/M20221112_server_password.hs @@ -0,0 +1,12 @@ +{-# LANGUAGE QuasiQuotes #-} + +module Simplex.Chat.Migrations.M20221112_server_password where + +import Database.SQLite.Simple (Query) +import Database.SQLite.Simple.QQ (sql) + +m20221112_server_password :: Query +m20221112_server_password = + [sql| +ALTER TABLE smp_servers ADD COLUMN basic_auth TEXT; +|] diff --git a/src/Simplex/Chat/Migrations/chat_schema.sql b/src/Simplex/Chat/Migrations/chat_schema.sql index b4c1d7676e..957573dff0 100644 --- a/src/Simplex/Chat/Migrations/chat_schema.sql +++ b/src/Simplex/Chat/Migrations/chat_schema.sql @@ -384,6 +384,7 @@ CREATE TABLE smp_servers( user_id INTEGER NOT NULL REFERENCES users ON DELETE CASCADE, created_at TEXT NOT NULL DEFAULT(datetime('now')), updated_at TEXT NOT NULL DEFAULT(datetime('now')), + basic_auth TEXT, UNIQUE(host, port) ); CREATE INDEX idx_messages_shared_msg_id ON messages(shared_msg_id); diff --git a/src/Simplex/Chat/Options.hs b/src/Simplex/Chat/Options.hs index 8f2bbfd0f5..6d618ef627 100644 --- a/src/Simplex/Chat/Options.hs +++ b/src/Simplex/Chat/Options.hs @@ -16,7 +16,7 @@ import qualified Data.Attoparsec.ByteString.Char8 as A import qualified Data.ByteString.Char8 as B import Options.Applicative import Simplex.Chat.Controller (updateStr, versionStr) -import Simplex.Messaging.Agent.Protocol (SMPServer) +import Simplex.Messaging.Agent.Protocol (SMPServerWithAuth) import Simplex.Messaging.Client (NetworkConfig (..), defaultNetworkConfig) import Simplex.Messaging.Encoding.String import Simplex.Messaging.Parsers (parseAll) @@ -26,7 +26,7 @@ import System.FilePath (combine) data ChatOpts = ChatOpts { dbFilePrefix :: String, dbKey :: String, - smpServers :: [SMPServer], + smpServers :: [SMPServerWithAuth], networkConfig :: NetworkConfig, logConnections :: Bool, logServerHosts :: Bool, @@ -155,7 +155,7 @@ fullNetworkConfig socksProxy tcpTimeout = let tcpConnectTimeout = (tcpTimeout * 3) `div` 2 in defaultNetworkConfig {socksProxy, tcpTimeout, tcpConnectTimeout} -parseSMPServers :: ReadM [SMPServer] +parseSMPServers :: ReadM [SMPServerWithAuth] parseSMPServers = eitherReader $ parseAll smpServersP . B.pack parseSocksProxy :: ReadM (Maybe SocksProxy) @@ -167,7 +167,7 @@ parseServerPort = eitherReader $ parseAll serverPortP . B.pack serverPortP :: A.Parser (Maybe String) serverPortP = Just . B.unpack <$> A.takeWhile A.isDigit -smpServersP :: A.Parser [SMPServer] +smpServersP :: A.Parser [SMPServerWithAuth] smpServersP = strP `A.sepBy1` A.char ';' getChatOpts :: FilePath -> FilePath -> IO ChatOpts diff --git a/src/Simplex/Chat/Store.hs b/src/Simplex/Chat/Store.hs index 121f08cf45..e506615741 100644 --- a/src/Simplex/Chat/Store.hs +++ b/src/Simplex/Chat/Store.hs @@ -252,6 +252,7 @@ import Data.Maybe (fromMaybe, isJust, isNothing, listToMaybe) import Data.Ord (Down (..)) import Data.Text (Text) import qualified Data.Text as T +import Data.Text.Encoding (encodeUtf8) import Data.Time.Clock (UTCTime (..), getCurrentTime) import Data.Time.LocalTime (TimeZone, getCurrentTimeZone) import Data.Type.Equality @@ -295,6 +296,7 @@ import Simplex.Chat.Migrations.M20221021_auto_accept__group_links import Simplex.Chat.Migrations.M20221024_contact_used import Simplex.Chat.Migrations.M20221025_chat_settings import Simplex.Chat.Migrations.M20221029_group_link_id +import Simplex.Chat.Migrations.M20221112_server_password import Simplex.Chat.Protocol import Simplex.Chat.Types import Simplex.Messaging.Agent.Protocol (ACorrId, AgentMsgId, ConnId, InvitationId, MsgMeta (..)) @@ -302,9 +304,9 @@ import Simplex.Messaging.Agent.Store.SQLite (SQLiteStore (..), createSQLiteStore import Simplex.Messaging.Agent.Store.SQLite.Migrations (Migration (..)) import qualified Simplex.Messaging.Crypto as C import Simplex.Messaging.Parsers (dropPrefix, sumTypeJSON) -import Simplex.Messaging.Protocol (ProtocolServer (..), SMPServer, pattern SMPServer) +import Simplex.Messaging.Protocol (BasicAuth (..), ProtoServerWithAuth (..), ProtocolServer (..), SMPServerWithAuth, pattern SMPServer) import Simplex.Messaging.Transport.Client (TransportHost) -import Simplex.Messaging.Util (eitherToMaybe) +import Simplex.Messaging.Util (eitherToMaybe, safeDecodeUtf8) import UnliftIO.STM schemaMigrations :: [(String, Query)] @@ -341,7 +343,8 @@ schemaMigrations = ("20221021_auto_accept__group_links", m20221021_auto_accept__group_links), ("20221024_contact_used", m20221024_contact_used), ("20221025_chat_settings", m20221025_chat_settings), - ("20221029_group_link_id", m20221029_group_link_id) + ("20221029_group_link_id", m20221029_group_link_id), + ("20221112_server_password", m20221112_server_password) ] -- | The list of migrations in ascending order by date @@ -4234,35 +4237,35 @@ toGroupChatItemList tz currentTs userContactId (((Just itemId, Just itemTs, Just either (const []) (: []) $ toGroupChatItem tz currentTs userContactId (((itemId, itemTs, itemContent, itemText, itemStatus, sharedMsgId, itemDeleted, itemEdited, createdAt, updatedAt) :. fileRow) :. memberRow_ :. quoteRow :. quotedMemberRow_) toGroupChatItemList _ _ _ _ = [] -getSMPServers :: DB.Connection -> User -> IO [SMPServer] +getSMPServers :: DB.Connection -> User -> IO [SMPServerWithAuth] getSMPServers db User {userId} = map toSmpServer <$> DB.query db [sql| - SELECT host, port, key_hash + SELECT host, port, key_hash, basic_auth FROM smp_servers WHERE user_id = ?; |] (Only userId) where - toSmpServer :: (NonEmpty TransportHost, String, C.KeyHash) -> SMPServer - toSmpServer (host, port, keyHash) = SMPServer host port keyHash + toSmpServer :: (NonEmpty TransportHost, String, C.KeyHash, Maybe Text) -> SMPServerWithAuth + toSmpServer (host, port, keyHash, auth_) = ProtoServerWithAuth (SMPServer host port keyHash) (BasicAuth . encodeUtf8 <$> auth_) -overwriteSMPServers :: DB.Connection -> User -> [SMPServer] -> ExceptT StoreError IO () +overwriteSMPServers :: DB.Connection -> User -> [SMPServerWithAuth] -> ExceptT StoreError IO () overwriteSMPServers db User {userId} smpServers = checkConstraint SEUniqueID . ExceptT $ do currentTs <- getCurrentTime DB.execute db "DELETE FROM smp_servers WHERE user_id = ?" (Only userId) - forM_ smpServers $ \ProtocolServer {host, port, keyHash} -> + forM_ smpServers $ \(ProtoServerWithAuth ProtocolServer {host, port, keyHash} auth_) -> DB.execute db [sql| INSERT INTO smp_servers - (host, port, key_hash, user_id, created_at, updated_at) - VALUES (?,?,?,?,?,?) + (host, port, key_hash, basic_auth, user_id, created_at, updated_at) + VALUES (?,?,?,?,?,?,?) |] - (host, port, keyHash, userId, currentTs, currentTs) + (host, port, keyHash, safeDecodeUtf8 . unBasicAuth <$> auth_, userId, currentTs, currentTs) pure $ Right () createCall :: DB.Connection -> User -> Call -> UTCTime -> IO () diff --git a/src/Simplex/Chat/View.hs b/src/Simplex/Chat/View.hs index 52fa614c2d..c3d03f4159 100644 --- a/src/Simplex/Chat/View.hs +++ b/src/Simplex/Chat/View.hs @@ -618,7 +618,7 @@ viewUserProfile Profile {displayName, fullName} = "(the updated profile will be sent to all your contacts)" ] -viewSMPServers :: [SMPServer] -> Bool -> [StyledString] +viewSMPServers :: [SMPServerWithAuth] -> Bool -> [StyledString] viewSMPServers smpServers testView = if testView then [customSMPServers] @@ -675,7 +675,7 @@ viewConnectionStats ConnectionStats {rcvServers, sndServers} = ["receiving messages via: " <> viewServerHosts rcvServers | not $ null rcvServers] <> ["sending messages via: " <> viewServerHosts sndServers | not $ null sndServers] -viewServers :: [SMPServer] -> StyledString +viewServers :: [SMPServerWithAuth] -> StyledString viewServers = plain . intercalate ", " . map (B.unpack . strEncode) viewServerHosts :: [SMPServer] -> StyledString diff --git a/stack.yaml b/stack.yaml index 0e68fe2974..2d363c7420 100644 --- a/stack.yaml +++ b/stack.yaml @@ -49,7 +49,7 @@ extra-deps: # - simplexmq-1.0.0@sha256:34b2004728ae396e3ae449cd090ba7410781e2b3cefc59259915f4ca5daa9ea8,8561 # - ../simplexmq - github: simplex-chat/simplexmq - commit: d2b88a1baa390ec64b6535e32ce69f26f53f4d7a + commit: 95db734b2d89bdf35e413f0abd4eac4ed3c64fc3 # - ../direct-sqlcipher - github: simplex-chat/direct-sqlcipher commit: 34309410eb2069b029b8fc1872deb1e0db123294 diff --git a/tests/ChatClient.hs b/tests/ChatClient.hs index 7dc479496e..11ab2771d7 100644 --- a/tests/ChatClient.hs +++ b/tests/ChatClient.hs @@ -51,7 +51,7 @@ testOpts = { dbFilePrefix = undefined, dbKey = "", -- dbKey = "this is a pass-phrase to encrypt the database", - smpServers = ["smp://LcJUMfVhwD8yxjAiSaDzzGF3-kLG4Uh0Fl_ZIjrRwjI=@localhost:5001"], + smpServers = ["smp://LcJUMfVhwD8yxjAiSaDzzGF3-kLG4Uh0Fl_ZIjrRwjI=:server_password@localhost:5001"], networkConfig = defaultNetworkConfig, logConnections = False, logServerHosts = False, @@ -275,6 +275,7 @@ serverCfg = storeLogFile = Nothing, storeMsgsFile = Nothing, allowNewQueues = True, + newQueueBasicAuth = Nothing, -- Just "server_password", messageExpiration = Just defaultMessageExpiration, inactiveClientExpiration = Just defaultInactiveClientExpiration, caCertificateFile = "tests/fixtures/tls/ca.crt", diff --git a/tests/ChatTests.hs b/tests/ChatTests.hs index b4d717047d..a41fd21f76 100644 --- a/tests/ChatTests.hs +++ b/tests/ChatTests.hs @@ -2869,6 +2869,8 @@ testGetSetSMPServers = alice #$> ("/smp_servers", id, "no custom SMP servers saved") alice #$> ("/smp_servers smp://1234-w==@smp1.example.im", id, "ok") alice #$> ("/smp_servers", id, "smp://1234-w==@smp1.example.im") + alice #$> ("/smp_servers smp://1234-w==:password@smp1.example.im", id, "ok") + alice #$> ("/smp_servers", id, "smp://1234-w==:password@smp1.example.im") alice #$> ("/smp_servers smp://2345-w==@smp2.example.im;smp://3456-w==@smp3.example.im:5224", id, "ok") alice #$> ("/smp_servers", id, "smp://2345-w==@smp2.example.im, smp://3456-w==@smp3.example.im:5224") alice #$> ("/smp_servers default", id, "ok")