Skip to content

Commit

Permalink
Introduce "mixed" protocol (#3258)
Browse files Browse the repository at this point in the history
* wire-api: add ProtocolMixedTag (with undefined stubs)

* wip

* wire-api

* fix bug in schema

* galley int test: fix some types

* libs/api-client fix type

* brig-integration: fix some types

* Add endpoint type

* wip

* add updateMixedProtocol

* wire-api add json instances to ProtocolTag

* add test

* Rename test group

* fix bug

* Add TODO

* Add failing test

* Add creator client to conversation

* Revert "Add TODO"

This reverts commit c3ed0c8.

* refactor tests and add failing test step

* Allow proteus messages on mixed protocol

* Add changelog entry

* Add nginz route

* hi ci

* hi ci

* finish leftover TODOs in protocolValidAction

* Fix bug in schema of ProtocolUpdate

* update docs: start-serices-only -> run-services

* Allow no-op state transitions
  • Loading branch information
smatting authored May 2, 2023
1 parent d69d207 commit 5b7458e
Show file tree
Hide file tree
Showing 31 changed files with 323 additions and 61 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -95,4 +95,4 @@ You have two options:

* Option 1. (recommended) Install wire-server on kubernetes using the configuration and instructions provided in [wire-server-deploy](https://github.com/wireapp/wire-server-deploy). This is the best option to run it on a server and recommended if you want to self-host wire-server.

* Option 2. Compile everything in this repo, then you can use the `services/start-services-only.sh`. This option is intended as a way to try out wire-server on your local development machine and not suited for production.
* Option 2. Compile everything in this repo, then you can use the `services/run-services`. This option is intended as a way to try out wire-server on your local development machine and not suited for production.
1 change: 1 addition & 0 deletions changelog.d/2-features/mixed-protocol
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Introduce a "mixed" conversation protocol type. A conversation of "mixed" protocol functions as a Proteus converation as well as a MLS conversations. It's intended to be used for migrating conversations from Proteus to MLS.
3 changes: 3 additions & 0 deletions charts/nginz/values.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,9 @@ nginx_conf:
- all
max_body_size: 40m
body_buffer_size: 256k
- path: /conversations/([^/]*)/([^/]*)/protocol
envs:
- all
- path: /broadcast
envs:
- all
Expand Down
2 changes: 1 addition & 1 deletion docs/src/developer/developer/how-to.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ Terminal 1:

Terminal 2:
* Compile all services: `make c`
* Run services including nginz: `./services/start-services-only.sh`.
* Run services including nginz: `./services/run-services`.

Open your browser at:
[http://localhost:8080/api/swagger-ui](http://localhost:8080/api/swagger-ui) for
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,6 @@ createConv users name = sessionRequest req rsc readBody
method POST
. path "conversations"
. acceptJson
. json (NewConv users [] (name >>= checked) mempty Nothing Nothing Nothing Nothing roleNameWireAdmin M.ProtocolProteusTag)
. json (NewConv users [] (name >>= checked) mempty Nothing Nothing Nothing Nothing roleNameWireAdmin M.ProtocolCreateProteusTag)
$ empty
rsc = status201 :| []
26 changes: 24 additions & 2 deletions libs/wire-api/src/Wire/API/Conversation.hs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,8 @@ module Wire.API.Conversation
maybeRole,

-- * create
ProtocolCreateTag (..),
protocolCreateToProtocolTag,
NewConv (..),
ConvTeamInfo (..),

Expand Down Expand Up @@ -632,6 +634,26 @@ instance ToSchema ReceiptMode where
--------------------------------------------------------------------------------
-- create

-- | This is distinct from 'ProtocolTag', which also include ProtocolMixedTag
data ProtocolCreateTag = ProtocolCreateProteusTag | ProtocolCreateMLSTag
deriving stock (Eq, Show, Enum, Bounded, Generic)
deriving (Arbitrary) via GenericUniform ProtocolCreateTag

instance ToSchema ProtocolCreateTag where
schema =
enum @Text "ProtocolCreateTag" $
mconcat
[ element "proteus" ProtocolCreateProteusTag,
element "mls" ProtocolCreateMLSTag
]

protocolCreateToProtocolTag :: ProtocolCreateTag -> ProtocolTag
protocolCreateToProtocolTag ProtocolCreateProteusTag = ProtocolProteusTag
protocolCreateToProtocolTag ProtocolCreateMLSTag = ProtocolMLSTag

protocolCreateTagSchema :: ObjectSchema SwaggerDoc ProtocolCreateTag
protocolCreateTagSchema = fmap (fromMaybe ProtocolCreateProteusTag) (optField "protocol" schema)

data NewConv = NewConv
{ newConvUsers :: [UserId],
-- | A list of qualified users, which can include some local qualified users
Expand All @@ -646,7 +668,7 @@ data NewConv = NewConv
-- | Every member except for the creator will have this role
newConvUsersRole :: RoleName,
-- | The protocol of the conversation. It can be Proteus or MLS (1.0).
newConvProtocol :: ProtocolTag
newConvProtocol :: ProtocolCreateTag
}
deriving stock (Eq, Show, Generic)
deriving (Arbitrary) via (GenericUniform NewConv)
Expand Down Expand Up @@ -705,7 +727,7 @@ newConvSchema sch =
.= ( fieldWithDocModifier "conversation_role" (description ?~ usersRoleDesc) schema
<|> pure roleNameWireAdmin
)
<*> newConvProtocol .= protocolTagSchema
<*> newConvProtocol .= protocolCreateTagSchema
where
usersDesc =
"List of user IDs (excluding the requestor) to be \
Expand Down
27 changes: 25 additions & 2 deletions libs/wire-api/src/Wire/API/Conversation/Protocol.hs
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,19 @@ module Wire.API.Conversation.Protocol
Epoch (..),
Protocol (..),
_ProtocolMLS,
_ProtocolMixed,
_ProtocolProteus,
protocolSchema,
ConversationMLSData (..),
ProtocolUpdate (..),
)
where

import Control.Arrow
import Control.Lens (makePrisms, (?~))
import Data.Aeson (FromJSON (..), ToJSON (..))
import Data.Schema
import qualified Data.Swagger as S
import Data.Time.Clock
import Imports
import Wire.API.Conversation.Action.Tag
Expand All @@ -45,7 +48,7 @@ import Wire.API.MLS.Group
import Wire.API.MLS.SubConversation
import Wire.Arbitrary

data ProtocolTag = ProtocolProteusTag | ProtocolMLSTag
data ProtocolTag = ProtocolProteusTag | ProtocolMLSTag | ProtocolMixedTag
deriving stock (Eq, Show, Enum, Bounded, Generic)
deriving (Arbitrary) via GenericUniform ProtocolTag

Expand Down Expand Up @@ -94,6 +97,7 @@ instance ToSchema ConversationMLSData where
data Protocol
= ProtocolProteus
| ProtocolMLS ConversationMLSData
| ProtocolMixed ConversationMLSData
deriving (Eq, Show, Generic)
deriving (Arbitrary) via GenericUniform Protocol

Expand All @@ -102,13 +106,15 @@ $(makePrisms ''Protocol)
protocolTag :: Protocol -> ProtocolTag
protocolTag ProtocolProteus = ProtocolProteusTag
protocolTag (ProtocolMLS _) = ProtocolMLSTag
protocolTag (ProtocolMixed _) = ProtocolMixedTag

-- | Certain actions need to be performed at the level of the underlying
-- protocol (MLS, mostly) before being applied to conversations. This function
-- returns whether a given action tag is directly applicable to a conversation
-- with the given protocol.
protocolValidAction :: Protocol -> ConversationActionTag -> Bool
protocolValidAction ProtocolProteus _ = True
protocolValidAction (ProtocolMixed _) _ = True
protocolValidAction (ProtocolMLS _) ConversationJoinTag = False
protocolValidAction (ProtocolMLS _) ConversationLeaveTag = True
protocolValidAction (ProtocolMLS _) ConversationRemoveMembersTag = False
Expand All @@ -120,9 +126,14 @@ instance ToSchema ProtocolTag where
enum @Text "Protocol" $
mconcat
[ element "proteus" ProtocolProteusTag,
element "mls" ProtocolMLSTag
element "mls" ProtocolMLSTag,
element "mixed" ProtocolMixedTag
]

deriving via (Schema ProtocolTag) instance FromJSON ProtocolTag

deriving via (Schema ProtocolTag) instance ToJSON ProtocolTag

protocolTagSchema :: ObjectSchema SwaggerDoc ProtocolTag
protocolTagSchema = fmap (fromMaybe ProtocolProteusTag) (optField "protocol" schema)

Expand All @@ -144,3 +155,15 @@ deriving via (Schema Protocol) instance ToJSON Protocol
protocolDataSchema :: ProtocolTag -> ObjectSchema SwaggerDoc Protocol
protocolDataSchema ProtocolProteusTag = tag _ProtocolProteus (pure ())
protocolDataSchema ProtocolMLSTag = tag _ProtocolMLS mlsDataSchema
protocolDataSchema ProtocolMixedTag = tag _ProtocolMixed mlsDataSchema

newtype ProtocolUpdate = ProtocolUpdate {unProtocolUpdate :: ProtocolTag}

instance ToSchema ProtocolUpdate where
schema = object "ProtocolUpdate" (ProtocolUpdate <$> unProtocolUpdate .= protocolTagSchema)

deriving via (Schema ProtocolUpdate) instance FromJSON ProtocolUpdate

deriving via (Schema ProtocolUpdate) instance ToJSON ProtocolUpdate

deriving via (Schema ProtocolUpdate) instance S.ToSchema ProtocolUpdate
3 changes: 3 additions & 0 deletions libs/wire-api/src/Wire/API/Error/Galley.hs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ data GalleyError
| InvalidTarget
| ConvNotFound
| ConvAccessDenied
| ConvInvalidProtocolTransition
| -- MLS Errors
MLSNotEnabled
| MLSNonEmptyMemberList
Expand Down Expand Up @@ -185,6 +186,8 @@ type instance MapError 'ConvNotFound = 'StaticError 404 "no-conversation" "Conve

type instance MapError 'ConvAccessDenied = 'StaticError 403 "access-denied" "Conversation access denied"

type instance MapError 'ConvInvalidProtocolTransition = 'StaticError 403 "invalid-protocol-transition" "Protocol transition is invalid"

type instance MapError 'InvalidTeamNotificationId = 'StaticError 400 "invalid-notification-id" "Could not parse notification id (must be UUIDv1)."

type instance
Expand Down
21 changes: 21 additions & 0 deletions libs/wire-api/src/Wire/API/Routes/Public/Galley/Conversation.hs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import Servant hiding (WithStatus)
import Servant.Swagger.Internal.Orphans ()
import Wire.API.Conversation
import Wire.API.Conversation.Code
import Wire.API.Conversation.Protocol
import Wire.API.Conversation.Role
import Wire.API.Conversation.Typing
import Wire.API.Error
Expand Down Expand Up @@ -1252,3 +1253,23 @@ type ConversationAPI =
'[RespondEmpty 200 "Update successful"]
()
)
:<|> Named
"update-conversation-protocol"
( Summary "Update the protocol of the conversation"
:> Description "**Note**: Only proteus->mixed upgrade is supported."
:> CanThrow 'ConvNotFound
:> CanThrow 'ConvInvalidProtocolTransition
:> CanThrow 'ConvMemberNotFound
:> ZLocalUser
:> ZClient
:> ZConn
:> "conversations"
:> QualifiedCapture' '[Description "Conversation ID"] "cnv" ConvId
:> "protocol"
:> ReqBody '[JSON] ProtocolUpdate
:> MultiVerb
'PUT
'[JSON]
'[RespondEmpty 200 "Update successful"]
()
)
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ import qualified Data.Set as Set (fromList)
import qualified Data.UUID as UUID (fromString)
import Imports
import Wire.API.Conversation
import Wire.API.Conversation.Protocol
import Wire.API.Conversation.Role

testDomain :: Domain
Expand All @@ -52,7 +51,7 @@ testObject_NewConv_user_1 =
newConvMessageTimer = Just (Ms {ms = 3320987366258987}),
newConvReceiptMode = Just (ReceiptMode {unReceiptMode = 1}),
newConvUsersRole = fromJust (parseRoleName "8tp2gs7b6"),
newConvProtocol = ProtocolProteusTag
newConvProtocol = ProtocolCreateProteusTag
}

testObject_NewConv_user_3 :: NewConv
Expand All @@ -71,5 +70,5 @@ testObject_NewConv_user_3 =
( parseRoleName
"y3otpiwu615lvvccxsq0315jj75jquw01flhtuf49t6mzfurvwe3_sh51f4s257e2x47zo85rif_xyiyfldpan3g4r6zr35rbwnzm0k"
),
newConvProtocol = ProtocolMLSTag
newConvProtocol = ProtocolCreateMLSTag
}
5 changes: 2 additions & 3 deletions services/brig/test/integration/API/OAuth.hs
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,9 @@ import Text.RawString.QQ
import URI.ByteString
import Util
import Web.FormUrlEncoded
import Wire.API.Conversation (Access (..), Conversation (cnvQualifiedId))
import Wire.API.Conversation (Access (..), Conversation (cnvQualifiedId), ProtocolCreateTag (..))
import qualified Wire.API.Conversation as Conv
import Wire.API.Conversation.Code (CreateConversationCodeRequest (CreateConversationCodeRequest))
import Wire.API.Conversation.Protocol (ProtocolTag (ProtocolProteusTag))
import qualified Wire.API.Conversation.Role as Role
import Wire.API.OAuth
import Wire.API.Routes.Bearer (Bearer (Bearer, unBearer))
Expand Down Expand Up @@ -703,7 +702,7 @@ createTeamConv ::
Http ResponseLBS
createTeamConv svc mkHeader token tid name = do
let tinfo = Conv.ConvTeamInfo tid
let conv = Conv.NewConv [] [] (checked name) (Set.fromList [CodeAccess]) Nothing (Just tinfo) Nothing Nothing Role.roleNameWireAdmin ProtocolProteusTag
let conv = Conv.NewConv [] [] (checked name) (Set.fromList [CodeAccess]) Nothing (Just tinfo) Nothing Nothing Role.roleNameWireAdmin ProtocolCreateProteusTag
post $
svc
. path "conversations"
Expand Down
3 changes: 1 addition & 2 deletions services/brig/test/integration/API/Provider.hs
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,6 @@ import Wire.API.Asset hiding (Asset)
import Wire.API.Connection
import Wire.API.Conversation
import Wire.API.Conversation.Bot
import Wire.API.Conversation.Protocol
import Wire.API.Conversation.Role
import Wire.API.Event.Conversation
import Wire.API.Internal.Notification
Expand Down Expand Up @@ -1415,7 +1414,7 @@ createConvWithAccessRoles ars g u us =
. contentJson
. body (RequestBodyLBS (encode conv))
where
conv = NewConv us [] Nothing Set.empty ars Nothing Nothing Nothing roleNameWireAdmin ProtocolProteusTag
conv = NewConv us [] Nothing Set.empty ars Nothing Nothing Nothing roleNameWireAdmin ProtocolCreateProteusTag

postMessage ::
Galley ->
Expand Down
3 changes: 1 addition & 2 deletions services/brig/test/integration/API/Team/Util.hs
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ import Test.Tasty.HUnit
import Util
import Web.Cookie (parseSetCookie, setCookieName)
import Wire.API.Conversation
import Wire.API.Conversation.Protocol
import Wire.API.Conversation.Role
import qualified Wire.API.Routes.Internal.Galley.TeamsIntra as Team
import Wire.API.Team hiding (newTeam)
Expand Down Expand Up @@ -235,7 +234,7 @@ createTeamConvWithRole role g tid u us mtimer = do
mtimer
Nothing
role
ProtocolProteusTag
ProtocolCreateProteusTag
r <-
post
( g
Expand Down
4 changes: 3 additions & 1 deletion services/brig/test/integration/Federation/End2end.hs
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@ testAddRemoteUsersToLocalConv brig1 galley1 brig2 galley2 = do
Nothing
Nothing
roleNameWireAdmin
ProtocolProteusTag
ProtocolCreateProteusTag
convId <-
fmap cnvQualifiedId . responseJsonError
=<< post
Expand Down Expand Up @@ -803,6 +803,7 @@ testSendMLSMessage brig1 brig2 galley1 galley2 cannon1 cannon2 = do
groupId <- case cnvProtocol conv of
ProtocolMLS p -> pure (unGroupId (cnvmlsGroupId p))
ProtocolProteus -> liftIO $ assertFailure "Expected MLS conversation"
ProtocolMixed _ -> liftIO $ assertFailure "Expected MLS conversation"
let qconvId = cnvQualifiedId conv
groupJSON <-
liftIO $
Expand Down Expand Up @@ -1066,6 +1067,7 @@ testSendMLSMessageToSubConversation brig1 brig2 galley1 galley2 cannon1 cannon2
groupId <- case cnvProtocol conv of
ProtocolMLS p -> pure (unGroupId (cnvmlsGroupId p))
ProtocolProteus -> liftIO $ assertFailure "Expected MLS conversation"
ProtocolMixed _ -> liftIO $ assertFailure "Expected MLS conversation"
let qconvId = cnvQualifiedId conv
groupJSON <-
liftIO $
Expand Down
5 changes: 2 additions & 3 deletions services/brig/test/integration/Util.hs
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,6 @@ import Util.Options
import Web.Internal.HttpApiData
import Wire.API.Connection
import Wire.API.Conversation
import Wire.API.Conversation.Protocol
import Wire.API.Conversation.Role (roleNameWireAdmin)
import Wire.API.Federation.API
import Wire.API.Federation.Domain
Expand Down Expand Up @@ -735,7 +734,7 @@ createMLSConversation galley zusr c = do
Nothing
Nothing
roleNameWireAdmin
ProtocolMLSTag
ProtocolCreateMLSTag
post $
galley
. path "/conversations"
Expand Down Expand Up @@ -776,7 +775,7 @@ createConversation galley zusr usersToAdd = do
Nothing
Nothing
roleNameWireAdmin
ProtocolProteusTag
ProtocolCreateProteusTag
post $
galley
. path "/conversations"
Expand Down
Loading

0 comments on commit 5b7458e

Please sign in to comment.