From ee9d82c34e2c6e6ea90255af3a3c6a7b921b08fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Dimja=C5=A1evi=C4=87?= Date: Wed, 13 Jul 2022 16:17:32 +0200 Subject: [PATCH 1/3] Move the Epoch type to a separate module - This should help with avoiding a dependency cycle in upcoming changes --- .../src/Wire/API/Conversation/Protocol.hs | 2 +- libs/wire-api/src/Wire/API/MLS/Epoch.hs | 32 +++++++++++++++++++ libs/wire-api/src/Wire/API/MLS/Message.hs | 12 ++----- libs/wire-api/test/unit/Test/Wire/API/MLS.hs | 1 + libs/wire-api/wire-api.cabal | 1 + .../src/Galley/Cassandra/Conversation/MLS.hs | 2 +- .../src/Galley/Effects/ConversationStore.hs | 2 +- 7 files changed, 39 insertions(+), 13 deletions(-) create mode 100644 libs/wire-api/src/Wire/API/MLS/Epoch.hs diff --git a/libs/wire-api/src/Wire/API/Conversation/Protocol.hs b/libs/wire-api/src/Wire/API/Conversation/Protocol.hs index c0bf4d79271..ee5b318788c 100644 --- a/libs/wire-api/src/Wire/API/Conversation/Protocol.hs +++ b/libs/wire-api/src/Wire/API/Conversation/Protocol.hs @@ -39,8 +39,8 @@ import Data.Schema import Imports import Wire.API.Arbitrary import Wire.API.Conversation.Action.Tag +import Wire.API.MLS.Epoch import Wire.API.MLS.Group -import Wire.API.MLS.Message data ProtocolTag = ProtocolProteusTag | ProtocolMLSTag deriving stock (Eq, Show, Enum, Bounded, Generic) diff --git a/libs/wire-api/src/Wire/API/MLS/Epoch.hs b/libs/wire-api/src/Wire/API/MLS/Epoch.hs new file mode 100644 index 00000000000..79c27bae432 --- /dev/null +++ b/libs/wire-api/src/Wire/API/MLS/Epoch.hs @@ -0,0 +1,32 @@ +{-# LANGUAGE GeneralizedNewtypeDeriving #-} + +-- This file is part of the Wire Server implementation. +-- +-- Copyright (C) 2022 Wire Swiss GmbH +-- +-- This program is free software: you can redistribute it and/or modify it under +-- the terms of the GNU Affero General Public License as published by the Free +-- Software Foundation, either version 3 of the License, or (at your option) any +-- later version. +-- +-- This program is distributed in the hope that it will be useful, but WITHOUT +-- ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +-- FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more +-- details. +-- +-- You should have received a copy of the GNU Affero General Public License along +-- with this program. If not, see . + +module Wire.API.MLS.Epoch where + +import Data.Schema +import Imports +import Wire.API.Arbitrary +import Wire.API.MLS.Serialisation + +newtype Epoch = Epoch {epochNumber :: Word64} + deriving stock (Eq, Show) + deriving newtype (Arbitrary, Enum, ToSchema) + +instance ParseMLS Epoch where + parseMLS = Epoch <$> parseMLS diff --git a/libs/wire-api/src/Wire/API/MLS/Message.hs b/libs/wire-api/src/Wire/API/MLS/Message.hs index ffb2c834277..6d81abf8253 100644 --- a/libs/wire-api/src/Wire/API/MLS/Message.hs +++ b/libs/wire-api/src/Wire/API/MLS/Message.hs @@ -20,8 +20,7 @@ -- with this program. If not, see . module Wire.API.MLS.Message - ( Epoch (..), - Message (..), + ( Message (..), WireFormatTag (..), SWireFormatTag (..), SomeMessage (..), @@ -35,23 +34,16 @@ module Wire.API.MLS.Message where import Data.Binary -import Data.Schema import Data.Singletons.TH import qualified Data.Swagger as S import Imports -import Wire.API.Arbitrary import Wire.API.MLS.Commit +import Wire.API.MLS.Epoch import Wire.API.MLS.Group import Wire.API.MLS.KeyPackage import Wire.API.MLS.Proposal import Wire.API.MLS.Serialisation -newtype Epoch = Epoch {epochNumber :: Word64} - deriving stock (Eq, Show) - deriving newtype (Arbitrary, Enum, ToSchema) - -instance ParseMLS Epoch where - parseMLS = Epoch <$> parseMLS data WireFormatTag = MLSPlainText | MLSCipherText deriving (Bounded, Enum, Eq, Show) diff --git a/libs/wire-api/test/unit/Test/Wire/API/MLS.hs b/libs/wire-api/test/unit/Test/Wire/API/MLS.hs index 755172c756f..51613740eb2 100644 --- a/libs/wire-api/test/unit/Test/Wire/API/MLS.hs +++ b/libs/wire-api/test/unit/Test/Wire/API/MLS.hs @@ -31,6 +31,7 @@ import Test.Tasty.HUnit import Wire.API.MLS.CipherSuite import Wire.API.MLS.Commit import Wire.API.MLS.Credential +import Wire.API.MLS.Epoch import Wire.API.MLS.Extension import Wire.API.MLS.KeyPackage import Wire.API.MLS.Message diff --git a/libs/wire-api/wire-api.cabal b/libs/wire-api/wire-api.cabal index 0ad6fb5e2e9..ec38617e77a 100644 --- a/libs/wire-api/wire-api.cabal +++ b/libs/wire-api/wire-api.cabal @@ -47,6 +47,7 @@ library Wire.API.MLS.CipherSuite Wire.API.MLS.Commit Wire.API.MLS.Credential + Wire.API.MLS.Epoch Wire.API.MLS.Extension Wire.API.MLS.Group Wire.API.MLS.KeyPackage diff --git a/services/galley/src/Galley/Cassandra/Conversation/MLS.hs b/services/galley/src/Galley/Cassandra/Conversation/MLS.hs index 607e3866096..7fda9519689 100644 --- a/services/galley/src/Galley/Cassandra/Conversation/MLS.hs +++ b/services/galley/src/Galley/Cassandra/Conversation/MLS.hs @@ -23,8 +23,8 @@ import Data.Time import qualified Galley.Cassandra.Queries as Cql import Galley.Data.Types import Imports +import Wire.API.MLS.Epoch import Wire.API.MLS.Group -import Wire.API.MLS.Message acquireCommitLock :: GroupId -> Epoch -> NominalDiffTime -> Client LockAcquired acquireCommitLock groupId epoch ttl = do diff --git a/services/galley/src/Galley/Effects/ConversationStore.hs b/services/galley/src/Galley/Effects/ConversationStore.hs index 0765a3f71fa..442d2cfe8c1 100644 --- a/services/galley/src/Galley/Effects/ConversationStore.hs +++ b/services/galley/src/Galley/Effects/ConversationStore.hs @@ -64,7 +64,7 @@ import Galley.Types.Conversations.Members import Imports import Polysemy import Wire.API.Conversation hiding (Conversation, Member) -import Wire.API.MLS.Message +import Wire.API.MLS.Epoch data ConversationStore m a where CreateConversationId :: ConversationStore m ConvId From 93d28b8330d8e13270818cdfaca466e8dc922dbc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Dimja=C5=A1evi=C4=87?= Date: Wed, 13 Jul 2022 16:45:52 +0200 Subject: [PATCH 2/3] Include timestamp for POST /mls/messages response --- libs/wire-api/src/Wire/API/MLS/Message.hs | 28 +++++++++++++++- .../src/Wire/API/Routes/Public/Galley.hs | 24 +++++++++++++- services/galley/src/Galley/API/MLS.hs | 1 + services/galley/src/Galley/API/MLS/Message.hs | 32 +++++++++++++++++-- .../galley/src/Galley/API/Public/Servant.hs | 1 + services/galley/test/integration/API/MLS.hs | 6 ++-- .../galley/test/integration/API/MLS/Util.hs | 5 +-- 7 files changed, 89 insertions(+), 8 deletions(-) diff --git a/libs/wire-api/src/Wire/API/MLS/Message.hs b/libs/wire-api/src/Wire/API/MLS/Message.hs index 6d81abf8253..9e1f97204dd 100644 --- a/libs/wire-api/src/Wire/API/MLS/Message.hs +++ b/libs/wire-api/src/Wire/API/MLS/Message.hs @@ -30,13 +30,19 @@ module Wire.API.MLS.Message Sender (..), MLSPlainTextSym0, MLSCipherTextSym0, + MLSMessageSendingStatus (..), ) where +import Control.Lens ((?~)) +import qualified Data.Aeson as A import Data.Binary +import Data.Json.Util +import Data.Schema import Data.Singletons.TH import qualified Data.Swagger as S import Imports +import Wire.API.Event.Conversation import Wire.API.MLS.Commit import Wire.API.MLS.Epoch import Wire.API.MLS.Group @@ -44,7 +50,6 @@ import Wire.API.MLS.KeyPackage import Wire.API.MLS.Proposal import Wire.API.MLS.Serialisation - data WireFormatTag = MLSPlainText | MLSCipherText deriving (Bounded, Enum, Eq, Show) @@ -159,3 +164,24 @@ instance ParseMLS MessagePayloadTBS where ApplicationMessageTag -> ApplicationMessage <$> parseMLSBytes @Word32 ProposalMessageTag -> ProposalMessage <$> parseMLS CommitMessageTag -> CommitMessage <$> parseMLS + +data MLSMessageSendingStatus = MLSMessageSendingStatus + { mmssEvents :: [Event], + mmssTime :: UTCTimeMillis + } + deriving (A.ToJSON, A.FromJSON, S.ToSchema) via Schema MLSMessageSendingStatus + +instance ToSchema MLSMessageSendingStatus where + schema = + object "MLSMessageSendingStatus" $ + MLSMessageSendingStatus + <$> mmssEvents + .= fieldWithDocModifier + "events" + (description ?~ "A list of events caused by sending the message.") + (array schema) + <*> mmssTime + .= fieldWithDocModifier + "time" + (description ?~ "The time of sending the message.") + schema diff --git a/libs/wire-api/src/Wire/API/Routes/Public/Galley.hs b/libs/wire-api/src/Wire/API/Routes/Public/Galley.hs index 6d47129186b..644d04e83b6 100644 --- a/libs/wire-api/src/Wire/API/Routes/Public/Galley.hs +++ b/libs/wire-api/src/Wire/API/Routes/Public/Galley.hs @@ -1313,8 +1313,9 @@ type MLSMessagingAPI = :> MultiVerb1 'POST '[JSON] (RespondEmpty 201 "Welcome message sent") ) :<|> Named - "mls-message" + "mls-message-v1" ( Summary "Post an MLS message" + :> Until 'V2 :> CanThrow 'ConvAccessDenied :> CanThrow 'ConvNotFound :> CanThrow 'MLSKeyPackageRefNotFound @@ -1332,6 +1333,27 @@ type MLSMessagingAPI = :> ReqBody '[MLS] (RawMLS SomeMessage) :> MultiVerb1 'POST '[JSON] (Respond 201 "Message sent" [Event]) ) + :<|> Named + "mls-message" + ( Summary "Post an MLS message" + :> From 'V2 + :> CanThrow 'ConvAccessDenied + :> CanThrow 'ConvNotFound + :> CanThrow 'MLSKeyPackageRefNotFound + :> CanThrow 'MLSClientMismatch + :> CanThrow 'MLSProtocolErrorTag + :> CanThrow 'MLSStaleMessage + :> CanThrow MLSProposalFailure + :> CanThrow 'MLSProposalNotFound + :> CanThrow 'MLSUnsupportedMessage + :> CanThrow 'MLSUnsupportedProposal + :> CanThrow 'LegalHoldNotEnabled + :> CanThrow 'MissingLegalholdConsent + :> "messages" + :> ZConn + :> ReqBody '[MLS] (RawMLS SomeMessage) + :> MultiVerb1 'POST '[JSON] (Respond 201 "Message sent" MLSMessageSendingStatus) + ) type MLSAPI = LiftNamed (ZLocalUser :> "mls" :> MLSMessagingAPI) diff --git a/services/galley/src/Galley/API/MLS.hs b/services/galley/src/Galley/API/MLS.hs index c2886cdc9bb..1e89f9bd590 100644 --- a/services/galley/src/Galley/API/MLS.hs +++ b/services/galley/src/Galley/API/MLS.hs @@ -19,6 +19,7 @@ module Galley.API.MLS ( postMLSWelcome, postMLSMessage, postMLSMessageFromLocalUser, + postMLSMessageFromLocalUserV1, ) where diff --git a/services/galley/src/Galley/API/MLS/Message.hs b/services/galley/src/Galley/API/MLS/Message.hs index fab451cf17e..977597689b3 100644 --- a/services/galley/src/Galley/API/MLS/Message.hs +++ b/services/galley/src/Galley/API/MLS/Message.hs @@ -18,6 +18,7 @@ module Galley.API.MLS.Message ( postMLSMessageFromLocalUser, + postMLSMessageFromLocalUserV1, postMLSMessage, MLSMessageStaticErrors, ) @@ -90,7 +91,7 @@ type MLSMessageStaticErrors = ErrorS 'MLSUnsupportedProposal ] -postMLSMessageFromLocalUser :: +postMLSMessageFromLocalUserV1 :: ( HasProposalEffects r, Members '[ Resource, @@ -110,10 +111,37 @@ postMLSMessageFromLocalUser :: ConnId -> RawMLS SomeMessage -> Sem r [Event] -postMLSMessageFromLocalUser lusr conn msg = +postMLSMessageFromLocalUserV1 lusr conn msg = map lcuEvent <$> postMLSMessage lusr (qUntagged lusr) (Just conn) msg +postMLSMessageFromLocalUser :: + ( HasProposalEffects r, + Members + '[ Resource, + Error FederationError, + ErrorS 'ConvAccessDenied, + ErrorS 'ConvNotFound, + Error InternalError, + ErrorS 'MLSUnsupportedMessage, + ErrorS 'MLSStaleMessage, + ErrorS 'MLSProposalNotFound, + ErrorS 'MissingLegalholdConsent, + TinyLog + ] + r + ) => + Local UserId -> + ConnId -> + RawMLS SomeMessage -> + Sem r MLSMessageSendingStatus +postMLSMessageFromLocalUser lusr conn msg = do + -- FUTUREWORK: Inline the body of 'postMLSMessageFromLocalUserV1' once version + -- V1 is dropped + events <- postMLSMessageFromLocalUserV1 lusr conn msg + t <- toUTCTimeMillis <$> input + pure $ MLSMessageSendingStatus events t + postMLSMessage :: ( HasProposalEffects r, Members diff --git a/services/galley/src/Galley/API/Public/Servant.hs b/services/galley/src/Galley/API/Public/Servant.hs index 0d78ea19bdb..6d64082700e 100644 --- a/services/galley/src/Galley/API/Public/Servant.hs +++ b/services/galley/src/Galley/API/Public/Servant.hs @@ -159,6 +159,7 @@ servantSitemap = mls :: API MLSAPI GalleyEffects mls = mkNamedAPI @"mls-welcome-message" postMLSWelcome + <@> mkNamedAPI @"mls-message-v1" postMLSMessageFromLocalUserV1 <@> mkNamedAPI @"mls-message" postMLSMessageFromLocalUser customBackend :: API CustomBackendAPI GalleyEffects diff --git a/services/galley/test/integration/API/MLS.hs b/services/galley/test/integration/API/MLS.hs index 3d8f25675e0..1aca628f1f3 100644 --- a/services/galley/test/integration/API/MLS.hs +++ b/services/galley/test/integration/API/MLS.hs @@ -24,6 +24,7 @@ import API.Util import Bilge hiding (head) import Bilge.Assert import Cassandra +import Control.Arrow import Control.Lens (view) import qualified Data.Aeson as Aeson import Data.Default @@ -58,6 +59,7 @@ import Wire.API.Event.Conversation import Wire.API.Federation.API.Common import Wire.API.Federation.API.Galley import Wire.API.MLS.Group (convToGroupId) +import Wire.API.MLS.Message import Wire.API.Message tests :: IO TestSetup -> TestTree @@ -578,12 +580,12 @@ testRemoteAppMessage = withSystemTempDirectory "mls" $ \tmp -> do . pClients $ bob ms -> assertFailure ("unmocked endpoint called: " <> cs ms) - (events :: [Event], reqs) <- withTempMockFederator' mock $ do + (events :: [Event], reqs) <- fmap (first mmssEvents) . withTempMockFederator' mock $ do galley <- viewGalley void $ postCommit MessagingSetup {creator = alice, users = [bob], ..} responseJsonError =<< post - ( galley . paths ["mls", "messages"] + ( galley . paths ["v2", "mls", "messages"] . zUser (qUnqualified (pUserId alice)) . zConn "conn" . content "message/mls" diff --git a/services/galley/test/integration/API/MLS/Util.hs b/services/galley/test/integration/API/MLS/Util.hs index 4f6618661b1..82cff69634c 100644 --- a/services/galley/test/integration/API/MLS/Util.hs +++ b/services/galley/test/integration/API/MLS/Util.hs @@ -50,6 +50,7 @@ import Wire.API.Conversation.Protocol import Wire.API.Event.Conversation import Wire.API.MLS.Credential import Wire.API.MLS.KeyPackage +import Wire.API.MLS.Message import Wire.API.MLS.Serialisation import Wire.API.User.Client import Wire.API.User.Client.Prekey @@ -393,9 +394,9 @@ claimKeyPackage brig claimant target = postCommit :: HasCallStack => MessagingSetup -> TestM [Event] postCommit MessagingSetup {..} = do galley <- viewGalley - responseJsonError + fmap mmssEvents . responseJsonError =<< post - ( galley . paths ["mls", "messages"] + ( galley . paths ["v2", "mls", "messages"] . zUser (qUnqualified (pUserId creator)) . zConn "conn" . content "message/mls" From bc000ee628eb69ddf25b3a9d509d4f301a1dac43 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marko=20Dimja=C5=A1evi=C4=87?= Date: Thu, 14 Jul 2022 09:57:17 +0200 Subject: [PATCH 3/3] Add a changelog --- changelog.d/1-api-changes/mls-message | 1 + 1 file changed, 1 insertion(+) create mode 100644 changelog.d/1-api-changes/mls-message diff --git a/changelog.d/1-api-changes/mls-message b/changelog.d/1-api-changes/mls-message new file mode 100644 index 00000000000..f5cd629f15b --- /dev/null +++ b/changelog.d/1-api-changes/mls-message @@ -0,0 +1 @@ +The response to POST /mls/messages adds a timestamp