Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 26 additions & 1 deletion cassandra-schema.cql
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,8 @@ CREATE TABLE galley_test.legalhold_pending_prekeys (
CREATE TABLE galley_test.group_id_conv_id (
group_id blob PRIMARY KEY,
conv_id uuid,
domain text
domain text,
subconv_id text
) WITH bloom_filter_fp_chance = 0.01
AND caching = {'keys': 'ALL', 'rows_per_partition': 'NONE'}
AND comment = ''
Expand Down Expand Up @@ -524,6 +525,30 @@ CREATE TABLE galley_test.mls_commit_locks (
AND read_repair_chance = 0.0
AND speculative_retry = '99PERCENTILE';

CREATE TABLE galley_test.subconversation (
conv_id uuid,
subconv_id text,
cipher_suite int,
epoch bigint,
group_id blob,
public_group_state blob,
PRIMARY KEY (conv_id, subconv_id)
) WITH CLUSTERING ORDER BY (subconv_id ASC)
AND bloom_filter_fp_chance = 0.01
AND caching = {'keys': 'ALL', 'rows_per_partition': 'NONE'}
AND comment = ''
AND compaction = {'class': 'org.apache.cassandra.db.compaction.SizeTieredCompactionStrategy', 'max_threshold': '32', 'min_threshold': '4'}
AND compression = {'chunk_length_in_kb': '64', 'class': 'org.apache.cassandra.io.compress.LZ4Compressor'}
AND crc_check_chance = 1.0
AND dclocal_read_repair_chance = 0.1
AND default_time_to_live = 0
AND gc_grace_seconds = 864000
AND max_index_interval = 2048
AND memtable_flush_period_in_ms = 0
AND min_index_interval = 128
AND read_repair_chance = 0.0
AND speculative_retry = '99PERCENTILE';

CREATE TABLE galley_test.team (
team uuid PRIMARY KEY,
binding boolean,
Expand Down
4 changes: 4 additions & 0 deletions libs/wire-api/src/Wire/API/Error/Galley.hs
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ data GalleyError
| MLSWelcomeMismatch
| MLSMissingGroupInfo
| MLSMissingSenderClient
| MLSUnexpectedSenderClient
| MLSSubConvUnsupportedConvType
| --
NoBindingTeamMembers
| NoBindingTeam
Expand Down Expand Up @@ -217,6 +219,8 @@ type instance MapError 'MLSMissingGroupInfo = 'StaticError 404 "mls-missing-grou

type instance MapError 'MLSMissingSenderClient = 'StaticError 403 "mls-missing-sender-client" "The client has to refresh their access token and provide their client ID"

type instance MapError 'MLSSubConvUnsupportedConvType = 'StaticError 403 "mls-subconv-unsupported-convtype" "MLS subconversations are only supported for regular conversations"

type instance MapError 'NoBindingTeamMembers = 'StaticError 403 "non-binding-team-members" "Both users must be members of the same binding team"

type instance MapError 'NoBindingTeam = 'StaticError 403 "no-binding-team" "Operation allowed only on binding teams"
Expand Down
32 changes: 31 additions & 1 deletion libs/wire-api/src/Wire/API/MLS/SubConversation.hs
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,23 @@

module Wire.API.MLS.SubConversation where

import Control.Lens (makePrisms)
import Control.Lens (makePrisms, (?~))
import Control.Lens.Tuple (_1)
import Control.Monad.Except
import Data.Aeson (FromJSON (..), ToJSON (..))
import qualified Data.Aeson as A
import Data.Id
import Data.Qualified
import Data.Schema
import qualified Data.Swagger as S
import qualified Data.Text as T
import Imports
import Servant (FromHttpApiData (..), ToHttpApiData (toQueryParam))
import Test.QuickCheck
import Wire.API.MLS.CipherSuite
import Wire.API.MLS.Credential
import Wire.API.MLS.Epoch
import Wire.API.MLS.Group
import Wire.Arbitrary

-- | An MLS subconversation ID, which identifies a subconversation within a
Expand All @@ -54,6 +60,30 @@ instance FromHttpApiData SubConvId where
instance ToHttpApiData SubConvId where
toQueryParam = unSubConvId

data PublicSubConversation = PublicSubConversation
{ pscParentConvId :: Qualified ConvId,
pscSubConvId :: SubConvId,
pscGroupId :: GroupId,
pscEpoch :: Epoch,
pscCipherSuite :: CipherSuiteTag,
pscMembers :: [ClientIdentity]
}
deriving (Eq, Show)
deriving (A.ToJSON, A.FromJSON, S.ToSchema) via (Schema PublicSubConversation)

instance ToSchema PublicSubConversation where
schema =
objectWithDocModifier
"PublicSubConversation"
(description ?~ "A MLS subconversation")
$ PublicSubConversation
<$> pscParentConvId .= field "parent_qualified_id" schema
<*> pscSubConvId .= field "subconv_id" schema
<*> pscGroupId .= field "group_id" schema
<*> pscEpoch .= field "epoch" schema
<*> pscCipherSuite .= field "cipher_suite" schema
<*> pscMembers .= field "members" (array schema)

data ConvOrSubTag = ConvTag | SubConvTag
deriving (Eq, Enum, Bounded)

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 @@ -32,6 +32,7 @@ import Wire.API.Error.Galley
import Wire.API.Event.Conversation
import Wire.API.MLS.PublicGroupState
import Wire.API.MLS.Servant
import Wire.API.MLS.SubConversation
import Wire.API.Routes.MultiVerb
import Wire.API.Routes.Named
import Wire.API.Routes.Public
Expand Down Expand Up @@ -375,6 +376,26 @@ type ConversationAPI =
Conversation
)
)
:<|> Named
"get-subconversation"
( Summary "Get information about an MLS subconversation"
:> CanThrow 'ConvNotFound
:> CanThrow 'ConvAccessDenied
:> CanThrow 'MLSSubConvUnsupportedConvType
:> ZLocalUser
:> "conversations"
:> QualifiedCapture "cnv" ConvId
:> "subconversations"
:> Capture "subconv" SubConvId
:> MultiVerb1
'GET
'[JSON]
( Respond
200
"Subconversation"
PublicSubConversation
)
)
-- This endpoint can lead to the following events being sent:
-- - ConvCreate event to members
-- TODO: add note: "On 201, the conversation ID is the `Location` header"
Expand Down
6 changes: 6 additions & 0 deletions libs/wire-api/test/golden/Test/Wire/API/Golden/Manual.hs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import Test.Wire.API.Golden.Manual.GroupId
import Test.Wire.API.Golden.Manual.ListConversations
import Test.Wire.API.Golden.Manual.QualifiedUserClientPrekeyMap
import Test.Wire.API.Golden.Manual.SearchResultContact
import Test.Wire.API.Golden.Manual.SubConversation
import Test.Wire.API.Golden.Manual.TeamSize
import Test.Wire.API.Golden.Manual.Token
import Test.Wire.API.Golden.Manual.UserClientPrekeyMap
Expand Down Expand Up @@ -139,5 +140,10 @@ tests =
[ (testObject_TeamSize_1, "testObject_TeamSize_1.json"),
(testObject_TeamSize_2, "testObject_TeamSize_2.json"),
(testObject_TeamSize_3, "testObject_TeamSize_3.json")
],
testGroup "PublicSubConversation" $
testObjects
[ (testObject_PublicSubConversation_1, "testObject_PublicSubConversation_1.json"),
(testObject_PublicSubConversation_2, "testObject_PublicSubConversation_2.json")
]
]
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
-- This file is part of the Wire Server implementation.
--
-- Copyright (C) 2022 Wire Swiss GmbH <opensource@wire.com>
--
-- 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 <https://www.gnu.org/licenses/>.

module Test.Wire.API.Golden.Manual.SubConversation
( testObject_PublicSubConversation_1,
testObject_PublicSubConversation_2,
)
where

import Data.Domain
import Data.Id
import Data.Qualified
import qualified Data.UUID as UUID
import Imports
import Wire.API.MLS.CipherSuite
import Wire.API.MLS.Credential
import Wire.API.MLS.Epoch
import Wire.API.MLS.Group
import Wire.API.MLS.SubConversation

subConvId1 :: SubConvId
subConvId1 = SubConvId "test_group"

subConvId2 :: SubConvId
subConvId2 = SubConvId "call"

domain :: Domain
domain = Domain "golden.example.com"

convId :: Qualified ConvId
convId =
Qualified
( Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000100000001"))
)
domain

testObject_PublicSubConversation_1 :: PublicSubConversation
testObject_PublicSubConversation_1 =
PublicSubConversation
convId
subConvId1
(GroupId "test_group")
(Epoch 5)
MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519
[]

testObject_PublicSubConversation_2 :: PublicSubConversation
testObject_PublicSubConversation_2 =
PublicSubConversation
convId
subConvId2
(GroupId "test_group_2")
(Epoch 0)
MLS_128_DHKEMX25519_AES128GCM_SHA256_Ed25519
[mkClientIdentity user cid]
where
user :: Qualified UserId
user =
Qualified
( Id (fromJust (UUID.fromString "00000000-0000-0007-0000-000a00000002"))
)
domain
cid = ClientId "deadbeef"
11 changes: 11 additions & 0 deletions libs/wire-api/test/golden/testObject_PublicSubConversation_1.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"cipher_suite": 1,
"epoch": 5,
"group_id": "dGVzdF9ncm91cA==",
"members": [],
"parent_qualified_id": {
"domain": "golden.example.com",
"id": "00000000-0000-0001-0000-000100000001"
},
"subconv_id": "test_group"
}
17 changes: 17 additions & 0 deletions libs/wire-api/test/golden/testObject_PublicSubConversation_2.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"cipher_suite": 1,
"epoch": 0,
"group_id": "dGVzdF9ncm91cF8y",
"members": [
{
"client_id": "deadbeef",
"domain": "golden.example.com",
"user_id": "00000000-0000-0007-0000-000a00000002"
}
],
"parent_qualified_id": {
"domain": "golden.example.com",
"id": "00000000-0000-0001-0000-000100000001"
},
"subconv_id": "call"
}
1 change: 1 addition & 0 deletions libs/wire-api/wire-api.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -530,6 +530,7 @@ test-suite wire-api-golden-tests
Test.Wire.API.Golden.Manual.ListConversations
Test.Wire.API.Golden.Manual.QualifiedUserClientPrekeyMap
Test.Wire.API.Golden.Manual.SearchResultContact
Test.Wire.API.Golden.Manual.SubConversation
Test.Wire.API.Golden.Manual.TeamSize
Test.Wire.API.Golden.Manual.Token
Test.Wire.API.Golden.Manual.UserClientPrekeyMap
Expand Down
2 changes: 2 additions & 0 deletions services/galley/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
, HsOpenSSL
, HsOpenSSL-x509-system
, hspec
, http-api-data
, http-client
, http-client-openssl
, http-client-tls
Expand Down Expand Up @@ -276,6 +277,7 @@ mkDerivation {
HsOpenSSL
HsOpenSSL-x509-system
hspec
http-api-data
http-client
http-client-openssl
http-client-tls
Expand Down
5 changes: 5 additions & 0 deletions services/galley/galley.cabal
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ library
Galley.API.MLS.Message
Galley.API.MLS.Propagate
Galley.API.MLS.Removal
Galley.API.MLS.SubConversation
Galley.API.MLS.Types
Galley.API.MLS.Util
Galley.API.MLS.Welcome
Expand Down Expand Up @@ -79,6 +80,7 @@ library
Galley.Cassandra.SearchVisibility
Galley.Cassandra.Services
Galley.Cassandra.Store
Galley.Cassandra.SubConversation
Galley.Cassandra.Team
Galley.Cassandra.TeamFeatures
Galley.Cassandra.TeamNotifications
Expand Down Expand Up @@ -108,6 +110,7 @@ library
Galley.Effects.SearchVisibilityStore
Galley.Effects.ServiceStore
Galley.Effects.SparAccess
Galley.Effects.SubConversationStore
Galley.Effects.TeamFeatureStore
Galley.Effects.TeamMemberStore
Galley.Effects.TeamNotificationStore
Expand Down Expand Up @@ -473,6 +476,7 @@ executable galley-integration
, HsOpenSSL
, HsOpenSSL-x509-system
, hspec
, http-api-data
, http-client
, http-client-openssl
, http-client-tls
Expand Down Expand Up @@ -692,6 +696,7 @@ executable galley-schema
V75_MLSGroupInfo
V76_ProposalOrigin
V77_MLSGroupMemberClient
V78_MLSSubconversation

hs-source-dirs: schema/src
default-extensions:
Expand Down
4 changes: 3 additions & 1 deletion services/galley/schema/src/Main.hs
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ import qualified V74_ExposeInvitationsToTeamAdmin
import qualified V75_MLSGroupInfo
import qualified V76_ProposalOrigin
import qualified V77_MLSGroupMemberClient
import qualified V78_MLSSubconversation

main :: IO ()
main = do
Expand Down Expand Up @@ -145,7 +146,8 @@ main = do
V74_ExposeInvitationsToTeamAdmin.migration,
V75_MLSGroupInfo.migration,
V76_ProposalOrigin.migration,
V77_MLSGroupMemberClient.migration
V77_MLSGroupMemberClient.migration,
V78_MLSSubconversation.migration
-- When adding migrations here, don't forget to update
-- 'schemaVersion' in Galley.Cassandra
-- (see also docs/developer/cassandra-interaction.md)
Expand Down
Loading