diff --git a/changelog.d/1-api-changes/self-member-id-qualified b/changelog.d/1-api-changes/self-member-id-qualified new file mode 100644 index 0000000000..0b7ac33985 --- /dev/null +++ b/changelog.d/1-api-changes/self-member-id-qualified @@ -0,0 +1,2 @@ +The member.self ID in conversation endpoints is qualified and available as +"qualified_id". The old unqualified "id" is still available. diff --git a/libs/api-bot/src/Network/Wire/Bot/Assert.hs b/libs/api-bot/src/Network/Wire/Bot/Assert.hs index 4140b3ff33..b94a1367c8 100644 --- a/libs/api-bot/src/Network/Wire/Bot/Assert.hs +++ b/libs/api-bot/src/Network/Wire/Bot/Assert.hs @@ -21,7 +21,7 @@ module Network.Wire.Bot.Assert where import Data.Id (ConvId, UserId) -import Data.Qualified (qUnqualified) +import Data.Qualified (Local, QualifiedWithTag (qUntagged), qUnqualified, qualifyAs) import qualified Data.Set as Set import Imports import Network.Wire.Bot.Monad @@ -31,7 +31,7 @@ import Network.Wire.Client.API.User assertConvCreated :: (HasCallStack, MonadBotNet m) => - ConvId -> + Local ConvId -> -- | The creator of the conversation. Bot -> -- | The other users in the conversation. @@ -41,14 +41,14 @@ assertConvCreated c b bs = do let everyone = b : bs forM_ bs $ \u -> let others = Set.fromList . filter (/= botId u) . map botId $ everyone - in assertEvent u TConvCreate (convCreate (botId u) others) + in assertEvent u TConvCreate (convCreate (qUntagged . qualifyAs c . botId $ u) others) where convCreate self others = \case EConvCreate e -> let cnv = convEvtData e mems = cnvMembers cnv omems = Set.fromList (map (qUnqualified . omQualifiedId) (cmOthers mems)) - in (qUnqualified . cnvQualifiedId $ cnv) == c + in cnvQualifiedId cnv == qUntagged c && convEvtFrom e == botId b && cnvType cnv == RegularConv && memId (cmSelf mems) == self diff --git a/libs/api-bot/src/Network/Wire/Bot/Monad.hs b/libs/api-bot/src/Network/Wire/Bot/Monad.hs index 5c78e8b851..45f65c4e3d 100644 --- a/libs/api-bot/src/Network/Wire/Bot/Monad.hs +++ b/libs/api-bot/src/Network/Wire/Bot/Monad.hs @@ -88,6 +88,7 @@ module Network.Wire.Bot.Monad BotNetException (..), BotNetFailure (..), try, + qualifyLocal, ) where @@ -104,6 +105,7 @@ import Data.Id import Data.Metrics (Metrics) import qualified Data.Metrics as Metrics import Data.Misc (PlainTextPassword (..)) +import Data.Qualified (Local, toLocalUnsafe) import Data.Text (pack, unpack) import Data.Time.Clock import Data.Time.Format (defaultTimeLocale, formatTime, parseTimeOrError) @@ -525,6 +527,9 @@ withCachedBot t f = do viewFederationDomain :: MonadBotNet m => m Domain viewFederationDomain = liftBotNet . BotNet $ asks botNetDomain +qualifyLocal :: MonadBotNet m => a -> m (Local a) +qualifyLocal a = toLocalUnsafe <$> viewFederationDomain <*> pure a + ------------------------------------------------------------------------------- -- Assertions diff --git a/libs/wire-api/src/Wire/API/Conversation/Member.hs b/libs/wire-api/src/Wire/API/Conversation/Member.hs index 7b1a570ff0..e2abc40d06 100644 --- a/libs/wire-api/src/Wire/API/Conversation/Member.hs +++ b/libs/wire-api/src/Wire/API/Conversation/Member.hs @@ -89,9 +89,8 @@ modelConversationMembers = Doc.defineModel "ConversationMembers" $ do -------------------------------------------------------------------------------- -- Members --- FUTUREWORK: Add a qualified Id here. data Member = Member - { memId :: UserId, + { memId :: Qualified UserId, memService :: Maybe ServiceRef, memOtrMutedStatus :: Maybe MutedStatus, memOtrMutedRef :: Maybe Text, @@ -109,7 +108,9 @@ instance ToSchema Member where schema = object "Member" $ Member - <$> memId .= field "id" schema + <$> memId .= field "qualified_id" schema + <* (qUnqualified . memId) + .= optional (field "id" (deprecatedSchema "qualified_id" schema)) <*> memService .= lax (field "service" (optWithDefault A.Null schema)) -- Remove ... <* const () .= optional (field "status" (c (0 :: Int))) diff --git a/libs/wire-api/test/golden/testObject_ConvMembers_user_1.json b/libs/wire-api/test/golden/testObject_ConvMembers_user_1.json index 67711be729..8c5765489d 100644 --- a/libs/wire-api/test/golden/testObject_ConvMembers_user_1.json +++ b/libs/wire-api/test/golden/testObject_ConvMembers_user_1.json @@ -23,6 +23,10 @@ "otr_archived_ref": "", "otr_muted_ref": "", "otr_muted_status": null, + "qualified_id": { + "domain": "golden.example.com", + "id": "00000000-0000-0001-0000-000100000001" + }, "service": { "id": "00000001-0000-0000-0000-000000000000", "provider": "00000000-0000-0000-0000-000000000000" diff --git a/libs/wire-api/test/golden/testObject_ConvMembers_user_2.json b/libs/wire-api/test/golden/testObject_ConvMembers_user_2.json index 97ecd589b7..050ea4b53a 100644 --- a/libs/wire-api/test/golden/testObject_ConvMembers_user_2.json +++ b/libs/wire-api/test/golden/testObject_ConvMembers_user_2.json @@ -9,6 +9,10 @@ "otr_archived_ref": null, "otr_muted_ref": "", "otr_muted_status": null, + "qualified_id": { + "domain": "golden.example.com", + "id": "00000000-0000-0000-0000-000000000000" + }, "service": { "id": "00000001-0000-0001-0000-000100000001", "provider": "00000001-0000-0001-0000-000000000001" diff --git a/libs/wire-api/test/golden/testObject_ConversationList_20Conversation_user_1.json b/libs/wire-api/test/golden/testObject_ConversationList_20Conversation_user_1.json index 326adbbcda..ceccfb14fc 100644 --- a/libs/wire-api/test/golden/testObject_ConversationList_20Conversation_user_1.json +++ b/libs/wire-api/test/golden/testObject_ConversationList_20Conversation_user_1.json @@ -18,6 +18,10 @@ "otr_archived_ref": null, "otr_muted_ref": "", "otr_muted_status": 0, + "qualified_id": { + "domain": "golden.example.com", + "id": "00000000-0000-0000-0000-000100000000" + }, "service": null, "status": 0, "status_ref": "0.0", diff --git a/libs/wire-api/test/golden/testObject_Conversation_user_1.json b/libs/wire-api/test/golden/testObject_Conversation_user_1.json index 5ac0de117e..22dcd63af6 100644 --- a/libs/wire-api/test/golden/testObject_Conversation_user_1.json +++ b/libs/wire-api/test/golden/testObject_Conversation_user_1.json @@ -16,6 +16,10 @@ "otr_archived_ref": "", "otr_muted_ref": null, "otr_muted_status": null, + "qualified_id": { + "domain": "golden.example.com", + "id": "00000001-0000-0001-0000-000100000000" + }, "service": null, "status": 0, "status_ref": "0.0", diff --git a/libs/wire-api/test/golden/testObject_Conversation_user_2.json b/libs/wire-api/test/golden/testObject_Conversation_user_2.json index 6b60594110..7c2362da23 100644 --- a/libs/wire-api/test/golden/testObject_Conversation_user_2.json +++ b/libs/wire-api/test/golden/testObject_Conversation_user_2.json @@ -43,6 +43,10 @@ "otr_archived_ref": null, "otr_muted_ref": null, "otr_muted_status": -1, + "qualified_id": { + "domain": "golden.example.com", + "id": "00000000-0000-0001-0000-000100000001" + }, "service": null, "status": 0, "status_ref": "0.0", diff --git a/libs/wire-api/test/golden/testObject_ConversationsResponse_1.json b/libs/wire-api/test/golden/testObject_ConversationsResponse_1.json index f33bf1205e..db6807e12d 100644 --- a/libs/wire-api/test/golden/testObject_ConversationsResponse_1.json +++ b/libs/wire-api/test/golden/testObject_ConversationsResponse_1.json @@ -28,6 +28,10 @@ "otr_archived_ref": "", "otr_muted_ref": null, "otr_muted_status": null, + "qualified_id": { + "domain": "golden.example.com", + "id": "00000001-0000-0001-0000-000100000000" + }, "service": null, "status": 0, "status_ref": "0.0", @@ -75,6 +79,10 @@ "otr_archived_ref": null, "otr_muted_ref": null, "otr_muted_status": -1, + "qualified_id": { + "domain": "golden.example.com", + "id": "00000000-0000-0001-0000-000100000001" + }, "service": null, "status": 0, "status_ref": "0.0", diff --git a/libs/wire-api/test/golden/testObject_Event_user_8.json b/libs/wire-api/test/golden/testObject_Event_user_8.json index bf7fab7331..ecfc1b3da8 100644 --- a/libs/wire-api/test/golden/testObject_Event_user_8.json +++ b/libs/wire-api/test/golden/testObject_Event_user_8.json @@ -36,6 +36,10 @@ "otr_archived_ref": "", "otr_muted_ref": "", "otr_muted_status": 0, + "qualified_id": { + "domain": "golden.example.com", + "id": "00000001-0000-0000-0000-000000000001" + }, "service": { "id": "00000001-0000-0000-0000-000000000001", "provider": "00000000-0000-0000-0000-000100000001" diff --git a/libs/wire-api/test/golden/testObject_Member_user_1.json b/libs/wire-api/test/golden/testObject_Member_user_1.json index 76d176b1be..16fad76423 100644 --- a/libs/wire-api/test/golden/testObject_Member_user_1.json +++ b/libs/wire-api/test/golden/testObject_Member_user_1.json @@ -7,6 +7,10 @@ "otr_archived_ref": "πΆ––", "otr_muted_ref": "ref", "otr_muted_status": -2, + "qualified_id": { + "domain": "golden.example.com", + "id": "00000002-0000-0001-0000-000100000000" + }, "service": { "id": "00000000-0000-0000-0000-000000000001", "provider": "00000000-0000-0001-0000-000000000001" diff --git a/libs/wire-api/test/golden/testObject_Member_user_2.json b/libs/wire-api/test/golden/testObject_Member_user_2.json index e6f6fd61ce..4415044d05 100644 --- a/libs/wire-api/test/golden/testObject_Member_user_2.json +++ b/libs/wire-api/test/golden/testObject_Member_user_2.json @@ -7,6 +7,10 @@ "otr_archived_ref": null, "otr_muted_ref": null, "otr_muted_status": null, + "qualified_id": { + "domain": "golden.example.com", + "id": "00000000-0000-0001-0000-000100000002" + }, "service": null, "status": 0, "status_ref": "0.0", diff --git a/libs/wire-api/test/unit/Test/Wire/API/Golden/Generated/ConvMembers_user.hs b/libs/wire-api/test/unit/Test/Wire/API/Golden/Generated/ConvMembers_user.hs index c0e49460fa..2666cd2061 100644 --- a/libs/wire-api/test/unit/Test/Wire/API/Golden/Generated/ConvMembers_user.hs +++ b/libs/wire-api/test/unit/Test/Wire/API/Golden/Generated/ConvMembers_user.hs @@ -50,7 +50,7 @@ testObject_ConvMembers_user_1 = ConvMembers { cmSelf = Member - { memId = Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000100000001")), + { memId = Qualified (Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000100000001"))) domain, memService = Just ( ServiceRef @@ -92,7 +92,7 @@ testObject_ConvMembers_user_2 = ConvMembers { cmSelf = Member - { memId = Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000000000000")), + { memId = Qualified (Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000000000000"))) domain, memService = Just ( ServiceRef diff --git a/libs/wire-api/test/unit/Test/Wire/API/Golden/Generated/ConversationList_20Conversation_user.hs b/libs/wire-api/test/unit/Test/Wire/API/Golden/Generated/ConversationList_20Conversation_user.hs index 6c17eda72f..e51e664e48 100644 --- a/libs/wire-api/test/unit/Test/Wire/API/Golden/Generated/ConversationList_20Conversation_user.hs +++ b/libs/wire-api/test/unit/Test/Wire/API/Golden/Generated/ConversationList_20Conversation_user.hs @@ -27,6 +27,9 @@ import Imports (Bool (False, True), Maybe (Just, Nothing), fromJust) import Wire.API.Conversation import Wire.API.Conversation.Role (parseRoleName) +domain :: Domain +domain = Domain "golden.example.com" + testObject_ConversationList_20Conversation_user_1 :: ConversationList Conversation testObject_ConversationList_20Conversation_user_1 = ConversationList @@ -48,7 +51,7 @@ testObject_ConversationList_20Conversation_user_1 = ConvMembers { cmSelf = Member - { memId = Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000100000000")), + { memId = Qualified (Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000100000000"))) domain, memService = Nothing, memOtrMutedStatus = Just (MutedStatus {fromMutedStatus = 0}), memOtrMutedRef = Just "", diff --git a/libs/wire-api/test/unit/Test/Wire/API/Golden/Generated/Conversation_user.hs b/libs/wire-api/test/unit/Test/Wire/API/Golden/Generated/Conversation_user.hs index 5b819cdd47..688f856d43 100644 --- a/libs/wire-api/test/unit/Test/Wire/API/Golden/Generated/Conversation_user.hs +++ b/libs/wire-api/test/unit/Test/Wire/API/Golden/Generated/Conversation_user.hs @@ -34,7 +34,7 @@ domain = Domain "golden.example.com" testObject_Conversation_user_1 :: Conversation testObject_Conversation_user_1 = Conversation - { cnvQualifiedId = Qualified (Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000000000000"))) (Domain "golden.example.com"), + { cnvQualifiedId = Qualified (Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000000000000"))) domain, cnvMetadata = ConversationMetadata { cnvmType = One2OneConv, @@ -50,7 +50,7 @@ testObject_Conversation_user_1 = ConvMembers { cmSelf = Member - { memId = Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000100000000")), + { memId = Qualified (Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000100000000"))) domain, memService = Nothing, memOtrMutedStatus = Nothing, memOtrMutedRef = Nothing, @@ -67,7 +67,7 @@ testObject_Conversation_user_1 = testObject_Conversation_user_2 :: Conversation testObject_Conversation_user_2 = Conversation - { cnvQualifiedId = Qualified (Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000000000002"))) (Domain "golden.example.com"), + { cnvQualifiedId = Qualified (Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000000000002"))) domain, cnvMetadata = ConversationMetadata { cnvmType = SelfConv, @@ -96,7 +96,7 @@ testObject_Conversation_user_2 = ConvMembers { cmSelf = Member - { memId = Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000100000001")), + { memId = Qualified (Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000100000001"))) domain, memService = Nothing, memOtrMutedStatus = Just (MutedStatus {fromMutedStatus = -1}), memOtrMutedRef = Nothing, diff --git a/libs/wire-api/test/unit/Test/Wire/API/Golden/Generated/Event_user.hs b/libs/wire-api/test/unit/Test/Wire/API/Golden/Generated/Event_user.hs index bf7223ebb7..93e7d7dbfe 100644 --- a/libs/wire-api/test/unit/Test/Wire/API/Golden/Generated/Event_user.hs +++ b/libs/wire-api/test/unit/Test/Wire/API/Golden/Generated/Event_user.hs @@ -159,7 +159,7 @@ testObject_Event_user_8 = ConvMembers { cmSelf = Member - { memId = Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000000000001")), + { memId = Qualified (Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000000000001"))) (Domain "golden.example.com"), memService = Just ( ServiceRef diff --git a/libs/wire-api/test/unit/Test/Wire/API/Golden/Generated/Member_user.hs b/libs/wire-api/test/unit/Test/Wire/API/Golden/Generated/Member_user.hs index 5621b21169..bbac8ae66a 100644 --- a/libs/wire-api/test/unit/Test/Wire/API/Golden/Generated/Member_user.hs +++ b/libs/wire-api/test/unit/Test/Wire/API/Golden/Generated/Member_user.hs @@ -16,17 +16,22 @@ -- with this program. If not, see . module Test.Wire.API.Golden.Generated.Member_user where +import Data.Domain (Domain (..)) import Data.Id (Id (Id)) +import Data.Qualified (Qualified (..)) import qualified Data.UUID as UUID (fromString) import Imports (Bool (False, True), Maybe (Just, Nothing), fromJust) import Wire.API.Conversation (Member (..), MutedStatus (MutedStatus, fromMutedStatus)) import Wire.API.Conversation.Role (parseRoleName) import Wire.API.Provider.Service (ServiceRef (ServiceRef, _serviceRefId, _serviceRefProvider)) +domain :: Domain +domain = Domain "golden.example.com" + testObject_Member_user_1 :: Member testObject_Member_user_1 = Member - { memId = Id (fromJust (UUID.fromString "00000002-0000-0001-0000-000100000000")), + { memId = Qualified (Id (fromJust (UUID.fromString "00000002-0000-0001-0000-000100000000"))) domain, memService = Just ( ServiceRef @@ -47,7 +52,7 @@ testObject_Member_user_1 = testObject_Member_user_2 :: Member testObject_Member_user_2 = Member - { memId = Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000100000002")), + { memId = Qualified (Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000100000002"))) domain, memService = Nothing, memOtrMutedStatus = Nothing, memOtrMutedRef = Nothing, diff --git a/libs/wire-api/test/unit/Test/Wire/API/Golden/Manual/ConversationsResponse.hs b/libs/wire-api/test/unit/Test/Wire/API/Golden/Manual/ConversationsResponse.hs index 534641eb82..1210c3687a 100644 --- a/libs/wire-api/test/unit/Test/Wire/API/Golden/Manual/ConversationsResponse.hs +++ b/libs/wire-api/test/unit/Test/Wire/API/Golden/Manual/ConversationsResponse.hs @@ -12,16 +12,19 @@ import Imports import Wire.API.Conversation import Wire.API.Conversation.Role +domain :: Domain +domain = Domain "golden.example.com" + testObject_ConversationsResponse_1 :: ConversationsResponse testObject_ConversationsResponse_1 = ConversationsResponse { crFound = [conv1, conv2], crNotFound = - [ Qualified (Id (fromJust (UUID.fromString "00000018-0000-0020-0000-000e00000002"))) (Domain "golden.example.com"), + [ Qualified (Id (fromJust (UUID.fromString "00000018-0000-0020-0000-000e00000002"))) domain, Qualified (Id (fromJust (UUID.fromString "00000018-0000-0020-0000-111111111112"))) (Domain "golden2.example.com") ], crFailed = - [ Qualified (Id (fromJust (UUID.fromString "00000018-4444-0020-0000-000e00000002"))) (Domain "golden.example.com"), + [ Qualified (Id (fromJust (UUID.fromString "00000018-4444-0020-0000-000e00000002"))) domain, Qualified (Id (fromJust (UUID.fromString "99999999-0000-0020-0000-111111111112"))) (Domain "golden3.example.com") ] } @@ -29,7 +32,7 @@ testObject_ConversationsResponse_1 = conv1 :: Conversation conv1 = Conversation - { cnvQualifiedId = Qualified (Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000000000000"))) (Domain "golden.example.com"), + { cnvQualifiedId = Qualified (Id (fromJust (UUID.fromString "00000001-0000-0000-0000-000000000000"))) domain, cnvMetadata = ConversationMetadata { cnvmType = One2OneConv, @@ -45,7 +48,7 @@ conv1 = ConvMembers { cmSelf = Member - { memId = Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000100000000")), + { memId = Qualified (Id (fromJust (UUID.fromString "00000001-0000-0001-0000-000100000000"))) domain, memService = Nothing, memOtrMutedStatus = Nothing, memOtrMutedRef = Nothing, @@ -62,7 +65,7 @@ conv1 = conv2 :: Conversation conv2 = Conversation - { cnvQualifiedId = Qualified (Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000000000002"))) (Domain "golden.example.com"), + { cnvQualifiedId = Qualified (Id (fromJust (UUID.fromString "00000000-0000-0000-0000-000000000002"))) domain, cnvMetadata = ConversationMetadata { cnvmType = SelfConv, @@ -91,7 +94,7 @@ conv2 = ConvMembers { cmSelf = Member - { memId = Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000100000001")), + { memId = Qualified (Id (fromJust (UUID.fromString "00000000-0000-0001-0000-000100000001"))) domain, memService = Nothing, memOtrMutedStatus = Just (MutedStatus {fromMutedStatus = -1}), memOtrMutedRef = Nothing, diff --git a/services/brig/test/integration/API/Provider.hs b/services/brig/test/integration/API/Provider.hs index 535023c279..fb3a09bbc0 100644 --- a/services/brig/test/integration/API/Provider.hs +++ b/services/brig/test/integration/API/Provider.hs @@ -489,33 +489,29 @@ testDeleteService config db brig galley cannon = withTestService config db brig -- Create a conversation u1 <- createUser "Ernie" brig u2 <- createUser "Bert" brig - let uid1 = userId u1 - quid1 = userQualifiedId u1 - localDomain = qDomain quid1 - uid2 = userId u2 + let uid2 = userId u2 + Qualified uid1 localDomain = userQualifiedId u1 + luid1 = toLocalUnsafe localDomain uid1 postConnection brig uid1 uid2 !!! const 201 === statusCode putConnection brig uid2 uid1 Accepted !!! const 200 === statusCode cnv <- responseJsonError =<< (createConv galley uid1 [uid2] do deleteService brig pid sid defProviderPassword !!! const 202 === statusCode - _ <- waitFor (5 # Second) not (isMember galley buid1 cid) - _ <- waitFor (5 # Second) not (isMember galley buid2 cid) + _ <- waitFor (5 # Second) not (isMember galley lbuid1 cid) + _ <- waitFor (5 # Second) not (isMember galley lbuid2 cid) getBotConv galley bid1 cid !!! const 404 === statusCode getBotConv galley bid2 cid !!! const 404 === statusCode - wsAssertMemberLeave ws qcid qbuid1 [qbuid1] - wsAssertMemberLeave ws qcid qbuid2 [qbuid2] + wsAssertMemberLeave ws qcid (qUntagged lbuid1) [qUntagged lbuid1] + wsAssertMemberLeave ws qcid (qUntagged lbuid2) [qUntagged lbuid2] -- The service should not be available getService brig pid sid !!! const 404 === statusCode @@ -598,9 +594,9 @@ testAddRemoveBotTeam config db brig galley cannon = withTestService config db br testBotTeamOnlyConv :: Config -> DB.ClientState -> Brig -> Galley -> Cannon -> Http () testBotTeamOnlyConv config db brig galley cannon = withTestService config db brig defServiceApp $ \sref buf -> do (u1, u2, _h, _tid, cid, pid, sid) <- prepareBotUsersTeam brig galley sref - let (uid1, uid2) = (userId u1, userId u2) - quid1 = userQualifiedId u1 - localDomain = qDomain quid1 + let uid2 = userId u2 + Qualified uid1 localDomain = userQualifiedId u1 + luid1 = toLocalUnsafe localDomain uid1 qcid = Qualified cid localDomain -- Make the conversation team-only and check that the bot can't be added -- to the conversation @@ -611,21 +607,20 @@ testBotTeamOnlyConv config db brig galley cannon = withTestService config db bri -- Make the conversation allowed for guests and add the bot successfully setAccessRole uid1 cid NonActivatedAccessRole bid <- addBotConv localDomain brig cannon uid1 uid2 cid pid sid buf - let buid = botUserId bid - qbuid = Qualified buid localDomain + let lbuid = qualifyAs luid1 . botUserId $ bid -- Make the conversation team-only again and check that the bot has been removed WS.bracketR cannon uid1 $ \ws -> do setAccessRole uid1 cid TeamAccessRole - _ <- waitFor (5 # Second) not (isMember galley buid cid) + _ <- waitFor (5 # Second) not (isMember galley lbuid cid) getBotConv galley bid cid !!! const 404 === statusCode svcAssertConvAccessUpdate buf - quid1 + (qUntagged luid1) (ConversationAccessData (Set.singleton InviteAccess) TeamAccessRole) qcid - svcAssertMemberLeave buf qbuid [qbuid] qcid - wsAssertMemberLeave ws qcid qbuid [qbuid] + svcAssertMemberLeave buf (qUntagged lbuid) [qUntagged lbuid] qcid + wsAssertMemberLeave ws qcid (qUntagged lbuid) [qUntagged lbuid] where setAccessRole uid cid role = updateConversationAccess galley uid cid [InviteAccess] role @@ -876,6 +871,7 @@ testWhitelistKickout localDomain config db brig galley cannon = do -- Create a team and a conversation (owner, tid) <- Team.createUserWithTeam brig let qowner = Qualified owner localDomain + lowner = toLocalUnsafe localDomain owner cid <- Team.createTeamConv galley tid owner [] Nothing let qcid = Qualified cid localDomain -- Create a service @@ -888,18 +884,17 @@ testWhitelistKickout localDomain config db brig galley cannon = do responseJsonError =<< (addBot brig owner pid sid cid do dewhitelistService brig owner tid pid sid - _ <- waitFor (2 # Second) not (isMember galley buid cid) + _ <- waitFor (2 # Second) not (isMember galley lbuid cid) getBotConv galley bid cid !!! const 404 === statusCode - wsAssertMemberLeave ws qcid qowner [qbuid] - svcAssertMemberLeave buf qowner [qbuid] qcid + wsAssertMemberLeave ws qcid qowner [qUntagged lbuid] + svcAssertMemberLeave buf qowner [qUntagged lbuid] qcid -- The bot should not get any further events liftIO $ timeout (2 # Second) (readChan buf) >>= \case @@ -1943,18 +1938,18 @@ testMessageBotUtil :: WS.Cannon -> Http () testMessageBotUtil quid uc cid pid sid sref buf brig galley cannon = do - let uid = qUnqualified quid - localDomain = qDomain quid + let Qualified uid localDomain = quid + luid = toLocalUnsafe localDomain uid qcid = Qualified cid localDomain -- Add bot to conversation _rs <- addBot brig uid pid sid cid do postBotMessage galley bid bc cid [(uid, uc, (toBase64Text "Hi User!"))] !!! const 201 === statusCode - wsAssertMessage ws qcid qbuid bc uc (toBase64Text "Hi User!") + wsAssertMessage ws qcid (qUntagged lbuid) bc uc (toBase64Text "Hi User!") -- The user replies postMessage galley uid uc cid [(buid, bc, (toBase64Text "Hi Bot"))] !!! const 201 === statusCode @@ -1981,10 +1976,10 @@ testMessageBotUtil quid uc cid pid sid sref buf brig galley cannon = do WS.bracketR cannon uid $ \ws -> do deleteService brig pid sid defProviderPassword !!! const 202 === statusCode - _ <- waitFor (5 # Second) not (isMember galley buid cid) + _ <- waitFor (5 # Second) not (isMember galley lbuid cid) getBotConv galley bid cid !!! const 404 === statusCode - wsAssertMemberLeave ws qcid qbuid [qbuid] + wsAssertMemberLeave ws qcid (qUntagged lbuid) [qUntagged lbuid] prepareBotUsersTeam :: HasCallStack => diff --git a/services/brig/test/integration/Util.hs b/services/brig/test/integration/Util.hs index c23223a0bb..5d6e47b80a 100644 --- a/services/brig/test/integration/Util.hs +++ b/services/brig/test/integration/Util.hs @@ -640,16 +640,16 @@ listConvs galley zusr convs = do . zConn "conn" . json (ListConversations convs) -isMember :: Galley -> UserId -> ConvId -> (MonadIO m, MonadHttp m) => m Bool +isMember :: Galley -> Local UserId -> ConvId -> (MonadIO m, MonadHttp m) => m Bool isMember g usr cnv = do res <- get $ g - . paths ["i", "conversations", toByteString' cnv, "members", toByteString' usr] + . paths ["i", "conversations", toByteString' cnv, "members", toByteString' (tUnqualified usr)] . expect2xx case responseJsonMaybe res of Nothing -> return False - Just m -> return (usr == memId m) + Just m -> return (qUntagged usr == memId m) getStatus :: HasCallStack => Brig -> UserId -> (MonadIO m, MonadHttp m) => m AccountStatus getStatus brig u = diff --git a/services/galley/src/Galley/API/Federation.hs b/services/galley/src/Galley/API/Federation.hs index 3f1a7f2433..284ad11cbe 100644 --- a/services/galley/src/Galley/API/Federation.hs +++ b/services/galley/src/Galley/API/Federation.hs @@ -111,7 +111,7 @@ onConversationCreated domain rc = do (qUntagged (FederationAPIGalley.rcRemoteOrigUserId qrcConnected)) (rcTime qrcConnected) (EdConversation c) - pushConversationEvent Nothing event [Public.memId mem] [] + pushConversationEvent Nothing event [qUnqualified . Public.memId $ mem] [] getConversations :: Domain -> GetConversationsRequest -> Galley GetConversationsResponse getConversations domain (GetConversationsRequest uid cids) = do diff --git a/services/galley/src/Galley/API/Mapping.hs b/services/galley/src/Galley/API/Mapping.hs index c9da03d9c9..f118658f04 100644 --- a/services/galley/src/Galley/API/Mapping.hs +++ b/services/galley/src/Galley/API/Mapping.hs @@ -30,7 +30,7 @@ import Control.Monad.Catch import Data.Domain (Domain) import Data.Id (UserId, idToText) import Data.Qualified -import Galley.API.Util (viewFederationDomain) +import Galley.API.Util (qualifyLocal) import Galley.App import qualified Galley.Data as Data import Galley.Data.Types (convId) @@ -48,8 +48,8 @@ import Wire.API.Federation.API.Galley -- Throws "bad-state" when the user is not part of the conversation. conversationView :: UserId -> Data.Conversation -> Galley Conversation conversationView uid conv = do - localDomain <- viewFederationDomain - let mbConv = conversationViewMaybe localDomain uid conv + luid <- qualifyLocal uid + let mbConv = conversationViewMaybe luid conv maybe memberNotFound pure mbConv where memberNotFound = do @@ -64,17 +64,17 @@ conversationView uid conv = do -- | View for a given user of a stored conversation. -- -- Returns 'Nothing' if the user is not part of the conversation. -conversationViewMaybe :: Domain -> UserId -> Data.Conversation -> Maybe Conversation -conversationViewMaybe localDomain uid conv = do - let (selfs, lothers) = partition ((uid ==) . lmId) (Data.convLocalMembers conv) +conversationViewMaybe :: Local UserId -> Data.Conversation -> Maybe Conversation +conversationViewMaybe luid conv = do + let (selfs, lothers) = partition ((tUnqualified luid ==) . lmId) (Data.convLocalMembers conv) rothers = Data.convRemoteMembers conv - self <- localMemberToSelf <$> listToMaybe selfs + self <- localMemberToSelf luid <$> listToMaybe selfs let others = - map (localMemberToOther localDomain) lothers + map (localMemberToOther (tDomain luid)) lothers <> map remoteMemberToOther rothers pure $ Conversation - (Qualified (convId conv) localDomain) + (qUntagged . qualifyAs luid . convId $ conv) (Data.convMetadata conv) (ConvMembers self others) @@ -84,7 +84,7 @@ conversationViewMaybe localDomain uid conv = do -- discard the conversation altogether. This should only happen if the remote -- backend is misbehaving. remoteConversationView :: - UserId -> + Local UserId -> MemberStatus -> Remote RemoteConversation -> Maybe Conversation @@ -93,8 +93,9 @@ remoteConversationView uid status (qUntagged -> Qualified rconv rDomain) = do others = rcmOthers mems self = localMemberToSelf + uid LocalMember - { lmId = uid, + { lmId = tUnqualified uid, lmService = Nothing, lmStatus = status, lmConvRoleName = rcmSelfRole mems @@ -130,10 +131,10 @@ conversationToRemote localDomain ruid conv = do -- | Convert a local conversation member (as stored in the DB) to a publicly -- facing 'Member' structure. -localMemberToSelf :: LocalMember -> Member -localMemberToSelf lm = +localMemberToSelf :: Local x -> LocalMember -> Member +localMemberToSelf loc lm = Member - { memId = lmId lm, + { memId = qUntagged . qualifyAs loc . lmId $ lm, memService = lmService lm, memOtrMutedStatus = msOtrMutedStatus st, memOtrMutedRef = msOtrMutedRef st, diff --git a/services/galley/src/Galley/API/Query.hs b/services/galley/src/Galley/API/Query.hs index e05ee41249..d98c99598d 100644 --- a/services/galley/src/Galley/API/Query.hs +++ b/services/galley/src/Galley/API/Query.hs @@ -161,13 +161,14 @@ getRemoteConversationsWithFailures :: Galley ([FailedGetConversation], [Public.Conversation]) getRemoteConversationsWithFailures zusr convs = do localDomain <- viewFederationDomain + lusr <- qualifyLocal zusr -- get self member statuses from the database statusMap <- Data.remoteConversationStatus zusr convs let remoteView :: Remote FederatedGalley.RemoteConversation -> Maybe Conversation remoteView rconv = Mapping.remoteConversationView - zusr + lusr ( Map.findWithDefault defMemberStatus (fmap FederatedGalley.rcnvId rconv) @@ -358,9 +359,10 @@ internalGetMemberH (cnv ::: usr) = do getLocalSelf :: UserId -> ConvId -> Galley (Maybe Public.Member) getLocalSelf usr cnv = do + lusr <- qualifyLocal usr alive <- Data.isConvAlive cnv if alive - then Mapping.localMemberToSelf <$$> Data.member cnv usr + then Mapping.localMemberToSelf lusr <$$> Data.member cnv usr else Nothing <$ Data.deleteConversation cnv getConversationMetaH :: ConvId -> Galley Response diff --git a/services/galley/src/Galley/API/Util.hs b/services/galley/src/Galley/API/Util.hs index 41274df90a..ca2735e14b 100644 --- a/services/galley/src/Galley/API/Util.hs +++ b/services/galley/src/Galley/API/Util.hs @@ -722,7 +722,7 @@ fromNewRemoteConversation loc rc@NewRemoteConversation {..} = toMember :: OtherMember -> Public.Member toMember m = Public.Member - { memId = qUnqualified . omQualifiedId $ m, + { memId = omQualifiedId m, memService = omService m, memOtrMutedStatus = Nothing, memOtrMutedRef = Nothing, diff --git a/services/galley/test/integration/API.hs b/services/galley/test/integration/API.hs index 82036ae0ad..c0b86690d6 100644 --- a/services/galley/test/integration/API.hs +++ b/services/galley/test/integration/API.hs @@ -287,7 +287,7 @@ postConvOk = do rsp <- postConv alice [bob, jane] (Just nameMaxSize) [] Nothing Nothing viewFederationDomain bobId <- randomId convId <- randomId let remoteDomain = Domain "far-away.example.com" @@ -1941,7 +1947,7 @@ testGetQualifiedRemoteConv = do bobAsOtherMember = OtherMember bobQ Nothing roleNameWireAdmin aliceAsLocal = LocalMember aliceId defMemberStatus Nothing roleNameWireAdmin aliceAsOtherMember = localMemberToOther (qDomain aliceQ) aliceAsLocal - aliceAsSelfMember = localMemberToSelf aliceAsLocal + aliceAsSelfMember = localMemberToSelf loc aliceAsLocal connectWithRemoteUser aliceId bobQ registerRemoteConv remoteConvId bobId Nothing (Set.fromList [aliceAsOtherMember]) @@ -2019,6 +2025,7 @@ testBulkGetQualifiedConvs = do localDomain <- viewFederationDomain aliceQ <- randomQualifiedUser let alice = qUnqualified aliceQ + lAlice = toLocalUnsafe localDomain alice bobId <- randomId carlId <- randomId deeId <- randomId @@ -2087,8 +2094,8 @@ testBulkGetQualifiedConvs = do let expectedFound = sortOn cnvQualifiedId - $ maybeToList (remoteConversationView alice defMemberStatus (toRemoteUnsafe remoteDomainA mockConversationA)) - <> maybeToList (remoteConversationView alice defMemberStatus (toRemoteUnsafe remoteDomainB mockConversationB)) + $ maybeToList (remoteConversationView lAlice defMemberStatus (toRemoteUnsafe remoteDomainA mockConversationA)) + <> maybeToList (remoteConversationView lAlice defMemberStatus (toRemoteUnsafe remoteDomainB mockConversationB)) <> [localConv] actualFound = sortOn cnvQualifiedId $ crFound convs assertEqual "found conversations" expectedFound actualFound @@ -2195,14 +2202,14 @@ postMembersOk2 :: TestM () postMembersOk2 = do alice <- randomUser bob <- randomUser - chuck <- randomUser - connectUsers alice (list1 bob [chuck]) - connectUsers bob (singleton chuck) - conv <- decodeConvId <$> postConv alice [bob, chuck] Nothing [] Nothing Nothing - postMembers bob (singleton chuck) conv !!! do + chuck <- randomQualifiedUser + connectUsers alice (list1 bob [qUnqualified chuck]) + connectUsers bob (singleton . qUnqualified $ chuck) + conv <- decodeConvId <$> postConv alice [bob, qUnqualified chuck] Nothing [] Nothing Nothing + postMembers bob (singleton . qUnqualified $ chuck) conv !!! do const 204 === statusCode const Nothing === responseBody - chuck' <- responseJsonUnsafe <$> (getSelfMember chuck conv (getSelfMember (qUnqualified chuck) conv chuck') @@ -2748,7 +2755,7 @@ putMemberOk update = do -- Expected member state let memberBob = Member - { memId = bob, + { memId = qbob, memService = Nothing, memOtrMutedStatus = mupOtrMuteStatus update, memOtrMutedRef = mupOtrMuteRef update, @@ -2818,7 +2825,7 @@ putRemoteConvMemberOk update = do -- Expected member state let memberAlice = Member - { memId = alice, + { memId = qalice, memService = Nothing, memOtrMutedStatus = mupOtrMuteStatus update, memOtrMutedRef = mupOtrMuteRef update, diff --git a/services/galley/test/integration/API/Federation.hs b/services/galley/test/integration/API/Federation.hs index c2cb673e5e..bee48dd1e1 100644 --- a/services/galley/test/integration/API/Federation.hs +++ b/services/galley/test/integration/API/Federation.hs @@ -175,7 +175,7 @@ onConvCreated = do WS.bracketR2 c alice charlie $ \(wsA, wsC) -> do registerRemoteConv qconv (qUnqualified qBob) (Just "gossip") requestMembers liftIO $ do - let expectedSelf = alice + let expectedSelf = qAlice expectedOthers = [(qBob, roleNameWireAdmin), (qDee, roleNameWireMember)] expectedFrom = qBob -- since Charlie is not connected to Bob; expect a conversation with Alice&Bob only @@ -251,7 +251,7 @@ addUnconnectedUsersOnly = do -- Remote Bob creates a conversation with local Alice registerRemoteConv qconv (qUnqualified qBob) (Just "gossip") requestMembers liftIO $ do - let expectedSelf = alice + let expectedSelf = qAlice expectedOthers = [(qBob, roleNameWireAdmin)] expectedFrom = qBob WS.assertMatch_ (5 # Second) wsA $ diff --git a/services/galley/test/integration/API/MessageTimer.hs b/services/galley/test/integration/API/MessageTimer.hs index d7b435cc24..b7d1c8a078 100644 --- a/services/galley/test/integration/API/MessageTimer.hs +++ b/services/galley/test/integration/API/MessageTimer.hs @@ -75,11 +75,12 @@ messageTimerInit :: messageTimerInit mtimer = do -- Create a conversation with a timer [alice, bob, jane] <- randomUsers 3 + qAlice <- Qualified <$> pure alice <*> viewFederationDomain connectUsers alice (list1 bob [jane]) rsp <- postConv alice [bob, jane] Nothing [] Nothing mtimer pure alice <*> viewFederationDomain connectUsers alice (list1 bob [jane]) rsp <- postConv alice [bob, jane] Nothing [] Nothing Nothing pure alice <*> viewFederationDomain connectUsers alice (list1 bob [jane]) rsp <- postConv alice [bob, jane] Nothing [] Nothing Nothing pure alice <*> viewFederationDomain connectUsers alice (singleton bob) rsp <- postO2OConv alice bob Nothing pure alice <*> viewFederationDomain connectUsers alice (singleton bob) rsp <- postConv alice [bob] Nothing [] Nothing Nothing do let update = ConversationMessageTimerUpdate timer1sec diff --git a/services/galley/test/integration/API/Roles.hs b/services/galley/test/integration/API/Roles.hs index a90682dd95..1c54d00d2e 100644 --- a/services/galley/test/integration/API/Roles.hs +++ b/services/galley/test/integration/API/Roles.hs @@ -94,7 +94,7 @@ handleConversationRoleAdmin = do let role = roleNameWireAdmin cid <- WS.bracketR3 c alice bob chuck $ \(wsA, wsB, wsC) -> do rsp <- postConvWithRole alice [bob, chuck] (Just "gossip") [] Nothing Nothing role - void $ assertConvWithRole rsp RegularConv alice alice [bob, chuck] (Just "gossip") Nothing role + void $ assertConvWithRole rsp RegularConv alice qalice [bob, chuck] (Just "gossip") Nothing role let cid = decodeConvId rsp qcid = Qualified cid localDomain -- Make sure everyone gets the correct event @@ -136,7 +136,7 @@ handleConversationRoleMember = do let role = roleNameWireMember cid <- WS.bracketR3 c alice bob chuck $ \(wsA, wsB, wsC) -> do rsp <- postConvWithRole alice [bob, chuck] (Just "gossip") [] Nothing Nothing role - void $ assertConvWithRole rsp RegularConv alice alice [bob, chuck] (Just "gossip") Nothing role + void $ assertConvWithRole rsp RegularConv alice qalice [bob, chuck] (Just "gossip") Nothing role let cid = decodeConvId rsp qcid = Qualified cid localDomain -- Make sure everyone gets the correct event diff --git a/services/galley/test/integration/API/Teams.hs b/services/galley/test/integration/API/Teams.hs index ebc335b9fc..7dfe2b4fbb 100644 --- a/services/galley/test/integration/API/Teams.hs +++ b/services/galley/test/integration/API/Teams.hs @@ -790,9 +790,10 @@ testAddTeamConvLegacy = do mem2 <- newTeamMember' p <$> Util.randomUser Util.connectUsers owner (list1 (mem1 ^. userId) [extern, mem2 ^. userId]) tid <- Util.createNonBindingTeam "foo" owner [mem2] - let allUserIds = [owner, extern, mem1 ^. userId, mem2 ^. userId] - WS.bracketRN c allUserIds $ \wss -> do - cid <- Util.createTeamConvLegacy owner tid allUserIds (Just "blaa") + allUserIds <- for [owner, extern, mem1 ^. userId, mem2 ^. userId] $ + \u -> Qualified <$> pure u <*> viewFederationDomain + WS.bracketRN c (qUnqualified <$> allUserIds) $ \wss -> do + cid <- Util.createTeamConvLegacy owner tid (qUnqualified <$> allUserIds) (Just "blaa") mapM_ (checkConvCreateEvent cid) wss -- All members become admin by default mapM_ (assertConvMemberWithRole roleNameWireAdmin cid) allUserIds @@ -802,7 +803,9 @@ testAddTeamConvWithRole :: TestM () testAddTeamConvWithRole = do c <- view tsCannon owner <- Util.randomUser + qOwner <- Qualified <$> pure owner <*> viewFederationDomain extern <- Util.randomUser + qExtern <- Qualified <$> pure extern <*> viewFederationDomain let p = Util.symmPermissions [CreateConversation, DoNotUseDeprecatedAddRemoveConvMember] mem1 <- newTeamMember' p <$> Util.randomUser mem2 <- newTeamMember' p <$> Util.randomUser @@ -813,13 +816,13 @@ testAddTeamConvWithRole = do cid2 <- Util.createTeamConvWithRole owner tid [extern] (Just "blaa") Nothing Nothing roleNameWireAdmin checkConvCreateEvent cid2 wsOwner checkConvCreateEvent cid2 wsExtern - mapM_ (assertConvMemberWithRole roleNameWireAdmin cid2) [owner, extern] + mapM_ (assertConvMemberWithRole roleNameWireAdmin cid2) [qOwner, qExtern] -- Regular conversation (using member role for participants): cid3 <- Util.createTeamConvWithRole owner tid [extern] (Just "blaa") Nothing Nothing roleNameWireMember checkConvCreateEvent cid3 wsOwner checkConvCreateEvent cid3 wsExtern - assertConvMemberWithRole roleNameWireAdmin cid3 owner - assertConvMemberWithRole roleNameWireMember cid3 extern + assertConvMemberWithRole roleNameWireAdmin cid3 qOwner + assertConvMemberWithRole roleNameWireMember cid3 qExtern -- mem2 is not a conversation member and no longer receives -- an event that a new team conversation has been created @@ -879,13 +882,18 @@ testAddTeamMemberToConv :: TestM () testAddTeamMemberToConv = do personalUser <- Util.randomUser ownerT1 <- Util.randomUser + qOwnerT1 <- Qualified <$> pure ownerT1 <*> viewFederationDomain let p = Util.symmPermissions [DoNotUseDeprecatedAddRemoveConvMember] mem1T1 <- newTeamMember' p <$> Util.randomUser + qMem1T1 <- Qualified <$> pure (mem1T1 ^. userId) <*> viewFederationDomain mem2T1 <- newTeamMember' p <$> Util.randomUser + qMem2T1 <- Qualified <$> pure (mem2T1 ^. userId) <*> viewFederationDomain mem3T1 <- newTeamMember' (Util.symmPermissions []) <$> Util.randomUser mem4T1 <- newTeamMember' (Util.symmPermissions []) <$> Util.randomUser ownerT2 <- Util.randomUser + qOwnerT2 <- Qualified <$> pure ownerT2 <*> viewFederationDomain mem1T2 <- newTeamMember' p <$> Util.randomUser + qMem1T2 <- Qualified <$> pure (mem1T2 ^. userId) <*> viewFederationDomain Util.connectUsers ownerT1 (list1 (mem1T1 ^. userId) [mem2T1 ^. userId, mem3T1 ^. userId, ownerT2, personalUser]) tidT1 <- Util.createNonBindingTeam "foo" ownerT1 [mem1T1, mem2T1, mem3T1] tidT2 <- Util.createBindingTeamInternal "foo" ownerT2 @@ -902,9 +910,9 @@ testAddTeamMemberToConv = do Util.assertNotConvMember (mem2T1 ^. userId) cidT1 -- OTOH, mem3T1 _can_ add another team member despite lacking the required team permission -- since conversation roles trump any team roles. Note that all users are admins by default - Util.assertConvMember ownerT1 cidT1 + Util.assertConvMember qOwnerT1 cidT1 Util.postMembers ownerT1 (list1 (mem2T1 ^. userId) []) cidT1 !!! const 200 === statusCode - Util.assertConvMember (mem2T1 ^. userId) cidT1 + Util.assertConvMember qMem2T1 cidT1 -- The following tests check the logic: users can add other users to a conversation -- iff: -- - *the adding user is connected to the users being added* @@ -913,24 +921,24 @@ testAddTeamMemberToConv = do -- Now we add someone from T2 that we are connected to Util.postMembers ownerT1 (list1 ownerT2 []) cidT1 !!! const 200 === statusCode - Util.assertConvMember ownerT2 cidT1 + Util.assertConvMember qOwnerT2 cidT1 -- And they can add their own team members Util.postMembers ownerT2 (list1 (mem1T2 ^. userId) []) cidT1 !!! const 200 === statusCode - Util.assertConvMember (mem1T2 ^. userId) cidT1 + Util.assertConvMember qMem1T2 cidT1 -- Still, they cannot add random members without a connection from T1, despite the conversation being "hosted" there Util.postMembers ownerT2 (list1 (mem4T1 ^. userId) []) cidT1 !!! const 403 === statusCode Util.assertNotConvMember (mem4T1 ^. userId) cidT1 -- Now let's look at convs hosted on team2 -- ownerT2 *is* connected to ownerT1 Util.postMembers ownerT2 (list1 ownerT1 []) cidT2 !!! const 200 === statusCode - Util.assertConvMember ownerT1 cidT2 + Util.assertConvMember qOwnerT1 cidT2 -- and mem1T2 is on the same team, but mem1T1 is *not* Util.postMembers ownerT2 (list1 (mem1T2 ^. userId) [mem1T1 ^. userId]) cidT2 !!! const 403 === statusCode Util.assertNotConvMember (mem1T1 ^. userId) cidT2 Util.assertNotConvMember (mem1T2 ^. userId) cidT2 -- mem1T2 is on the same team, so that is fine too Util.postMembers ownerT2 (list1 (mem1T2 ^. userId) []) cidT2 !!! const 200 === statusCode - Util.assertConvMember (mem1T2 ^. userId) cidT2 + Util.assertConvMember qMem1T2 cidT2 -- ownerT2 is *NOT* connected to mem3T1 and not on the same team, so should not be allowed to add Util.postMembers ownerT2 (list1 (mem3T1 ^. userId) []) cidT2 !!! const 403 === statusCode Util.assertNotConvMember (mem3T1 ^. userId) cidT2 @@ -938,19 +946,19 @@ testAddTeamMemberToConv = do -- Can add connected users Util.postMembers personalUser (list1 ownerT1 []) cidPersonal !!! const 200 === statusCode - Util.assertConvMember ownerT1 cidPersonal + Util.assertConvMember qOwnerT1 cidPersonal -- Can *not* add users that are *not* connected Util.postMembers personalUser (list1 ownerT2 []) cidPersonal !!! const 403 === statusCode Util.assertNotConvMember ownerT2 cidPersonal -- Users of the same team can add one another Util.postMembers ownerT1 (list1 (mem1T1 ^. userId) []) cidPersonal !!! const 200 === statusCode - Util.assertConvMember (mem1T1 ^. userId) cidPersonal + Util.assertConvMember qMem1T1 cidPersonal -- Users can not add across teams if *not* connected Util.postMembers (mem1T1 ^. userId) (list1 ownerT2 []) cidPersonal !!! const 403 === statusCode Util.assertNotConvMember ownerT2 cidPersonal -- Users *can* add across teams if *connected* Util.postMembers ownerT1 (list1 ownerT2 []) cidPersonal !!! const 200 === statusCode - Util.assertConvMember ownerT2 cidPersonal + Util.assertConvMember qOwnerT2 cidPersonal testUpdateTeamConv :: -- | Team role of the user who creates the conversation @@ -976,20 +984,24 @@ testDeleteTeam = do g <- view tsGalley c <- view tsCannon owner <- Util.randomUser + qOwner <- Qualified <$> pure owner <*> viewFederationDomain let p = Util.symmPermissions [DoNotUseDeprecatedAddRemoveConvMember] member <- newTeamMember' p <$> Util.randomUser + qMember <- Qualified <$> pure (member ^. userId) <*> viewFederationDomain extern <- Util.randomUser + qExtern <- Qualified <$> pure extern <*> viewFederationDomain + let members = [owner, member ^. userId] Util.connectUsers owner (list1 (member ^. userId) [extern]) tid <- Util.createNonBindingTeam "foo" owner [member] cid1 <- Util.createTeamConv owner tid [] (Just "blaa") Nothing Nothing cid2 <- Util.createTeamConv owner tid members (Just "blup") Nothing Nothing - Util.assertConvMember owner cid2 - Util.assertConvMember (member ^. userId) cid2 + Util.assertConvMember qOwner cid2 + Util.assertConvMember qMember cid2 Util.assertNotConvMember extern cid2 Util.postMembers owner (list1 extern []) cid1 !!! const 200 === statusCode - Util.assertConvMember owner cid1 - Util.assertConvMember extern cid1 + Util.assertConvMember qOwner cid1 + Util.assertConvMember qExtern cid1 Util.assertNotConvMember (member ^. userId) cid1 void . WS.bracketR3 c owner extern (member ^. userId) $ \(wsOwner, wsExtern, wsMember) -> do delete (g . paths ["teams", toByteString' tid] . zUser owner . zConn "conn") @@ -1164,20 +1176,23 @@ testDeleteTeamConv = do g <- view tsGalley c <- view tsCannon owner <- Util.randomUser + qOwner <- Qualified <$> pure owner <*> viewFederationDomain let p = Util.symmPermissions [DoNotUseDeprecatedDeleteConversation] member <- newTeamMember' p <$> Util.randomUser - let members = [owner, member ^. userId] + qMember <- Qualified <$> pure (member ^. userId) <*> viewFederationDomain + let members = [qOwner, qMember] extern <- Util.randomUser + qExtern <- Qualified <$> pure extern <*> viewFederationDomain Util.connectUsers owner (list1 (member ^. userId) [extern]) tid <- Util.createNonBindingTeam "foo" owner [member] cid1 <- Util.createTeamConv owner tid [] (Just "blaa") Nothing Nothing let access = ConversationAccessData (Set.fromList [InviteAccess, CodeAccess]) ActivatedAccessRole putAccessUpdate owner cid1 access !!! const 200 === statusCode code <- decodeConvCodeEvent <$> (postConvCode owner cid1 members) (Just "blup") Nothing Nothing Util.postMembers owner (list1 extern [member ^. userId]) cid1 !!! const 200 === statusCode - for_ [owner, member ^. userId, extern] $ \u -> Util.assertConvMember u cid1 - for_ [owner, member ^. userId] $ \u -> Util.assertConvMember u cid2 + for_ (qExtern : members) $ \u -> Util.assertConvMember u cid1 + for_ members $ flip Util.assertConvMember cid2 WS.bracketR3 c owner extern (member ^. userId) $ \(wsOwner, wsExtern, wsMember) -> do delete ( g diff --git a/services/galley/test/integration/API/Teams/LegalHold.hs b/services/galley/test/integration/API/Teams/LegalHold.hs index 4ba9581775..01d7cf9450 100644 --- a/services/galley/test/integration/API/Teams/LegalHold.hs +++ b/services/galley/test/integration/API/Teams/LegalHold.hs @@ -941,7 +941,9 @@ data GroupConvAdmin testNoConsentRemoveFromGroupConv :: GroupConvAdmin -> HasCallStack => TestM () testNoConsentRemoveFromGroupConv whoIsAdmin = do (legalholder :: UserId, tid) <- createBindingTeam + qLegalHolder <- Qualified <$> pure legalholder <*> viewFederationDomain (peer :: UserId, teamPeer) <- createBindingTeam + qPeer <- Qualified <$> pure peer <*> viewFederationDomain galley <- view tsGalley let enableLHForLegalholder :: HasCallStack => TestM () @@ -963,41 +965,40 @@ testNoConsentRemoveFromGroupConv whoIsAdmin = do convId <- do let (inviter, tidInviter, invitee, inviteeRole) = case whoIsAdmin of - LegalholderIsAdmin -> (legalholder, tid, peer, roleNameWireMember) - PeerIsAdmin -> (peer, teamPeer, legalholder, roleNameWireMember) - BothAreAdmins -> (legalholder, tid, peer, roleNameWireAdmin) + LegalholderIsAdmin -> (qLegalHolder, tid, qPeer, roleNameWireMember) + PeerIsAdmin -> (qPeer, teamPeer, qLegalHolder, roleNameWireMember) + BothAreAdmins -> (qLegalHolder, tid, qPeer, roleNameWireAdmin) - convId <- createTeamConvWithRole inviter tidInviter [invitee] (Just "group chat with external peer") Nothing Nothing inviteeRole + convId <- createTeamConvWithRole (qUnqualified inviter) tidInviter [qUnqualified invitee] (Just "group chat with external peer") Nothing Nothing inviteeRole mapM_ (assertConvMemberWithRole roleNameWireAdmin convId) ([inviter] <> [invitee | whoIsAdmin == BothAreAdmins]) mapM_ (assertConvMemberWithRole roleNameWireMember convId) [invitee | whoIsAdmin /= BothAreAdmins] pure convId + qconvId <- Qualified <$> pure convId <*> viewFederationDomain checkConvCreateEvent convId legalholderWs checkConvCreateEvent convId peerWs - assertConvMember legalholder convId - assertConvMember peer convId + assertConvMember qLegalHolder convId + assertConvMember qPeer convId void enableLHForLegalholder - localdomain <- viewFederationDomain - case whoIsAdmin of LegalholderIsAdmin -> do - assertConvMember legalholder convId + assertConvMember qLegalHolder convId assertNotConvMember peer convId - checkConvMemberLeaveEvent (Qualified convId localdomain) (Qualified peer localdomain) legalholderWs - checkConvMemberLeaveEvent (Qualified convId localdomain) (Qualified peer localdomain) peerWs + checkConvMemberLeaveEvent qconvId qPeer legalholderWs + checkConvMemberLeaveEvent qconvId qPeer peerWs PeerIsAdmin -> do - assertConvMember peer convId + assertConvMember qPeer convId assertNotConvMember legalholder convId - checkConvMemberLeaveEvent (Qualified convId localdomain) (Qualified legalholder localdomain) legalholderWs - checkConvMemberLeaveEvent (Qualified convId localdomain) (Qualified legalholder localdomain) peerWs + checkConvMemberLeaveEvent qconvId qLegalHolder legalholderWs + checkConvMemberLeaveEvent qconvId qLegalHolder peerWs BothAreAdmins -> do - assertConvMember legalholder convId + assertConvMember qLegalHolder convId assertNotConvMember peer convId - checkConvMemberLeaveEvent (Qualified convId localdomain) (Qualified peer localdomain) legalholderWs - checkConvMemberLeaveEvent (Qualified convId localdomain) (Qualified peer localdomain) peerWs + checkConvMemberLeaveEvent qconvId qPeer legalholderWs + checkConvMemberLeaveEvent qconvId qPeer peerWs data GroupConvInvCase = InviteOnlyConsenters | InviteAlsoNonConsenters deriving (Show, Eq, Ord, Bounded, Enum) @@ -1006,8 +1007,11 @@ testGroupConvInvitationHandlesLHConflicts :: HasCallStack => GroupConvInvCase -> testGroupConvInvitationHandlesLHConflicts inviteCase = do -- team that is legalhold whitelisted (legalholder :: UserId, tid) <- createBindingTeam + qLegalHolder <- Qualified <$> pure legalholder <*> viewFederationDomain userWithConsent <- (^. userId) <$> addUserToTeam legalholder tid - userWithConsent2 <- (^. userId) <$> addUserToTeam legalholder tid + userWithConsent2 <- do + uid <- (^. userId) <$> addUserToTeam legalholder tid + Qualified <$> pure uid <*> viewFederationDomain ensureQueueEmpty putLHWhitelistTeam tid !!! const 200 === statusCode @@ -1037,10 +1041,10 @@ testGroupConvInvitationHandlesLHConflicts inviteCase = do case inviteCase of InviteOnlyConsenters -> do - API.Util.postMembers userWithConsent (List1.list1 legalholder [userWithConsent2]) convId + API.Util.postMembers userWithConsent (List1.list1 legalholder [qUnqualified userWithConsent2]) convId !!! const 200 === statusCode - assertConvMember legalholder convId + assertConvMember qLegalHolder convId assertConvMember userWithConsent2 convId assertNotConvMember peer convId InviteAlsoNonConsenters -> do diff --git a/services/galley/test/integration/API/Util.hs b/services/galley/test/integration/API/Util.hs index a310a1b744..1fc2bf2465 100644 --- a/services/galley/test/integration/API/Util.hs +++ b/services/galley/test/integration/API/Util.hs @@ -1263,16 +1263,16 @@ registerRemoteConv convId originUser name othMembers = do ------------------------------------------------------------------------------- -- Common Assertions -assertConvMemberWithRole :: HasCallStack => RoleName -> ConvId -> UserId -> TestM () +assertConvMemberWithRole :: HasCallStack => RoleName -> ConvId -> Qualified UserId -> TestM () assertConvMemberWithRole r c u = - getSelfMember u c !!! do + getSelfMember (qUnqualified u) c !!! do const 200 === statusCode const (Right u) === (fmap memId <$> responseJsonEither) const (Right r) === (fmap memConvRoleName <$> responseJsonEither) -assertConvMember :: HasCallStack => UserId -> ConvId -> TestM () +assertConvMember :: HasCallStack => Qualified UserId -> ConvId -> TestM () assertConvMember u c = - getSelfMember u c !!! do + getSelfMember (qUnqualified u) c !!! do const 200 === statusCode const (Right u) === (fmap memId <$> responseJsonEither) @@ -1301,7 +1301,7 @@ assertConv :: Response (Maybe Lazy.ByteString) -> ConvType -> UserId -> - UserId -> + Qualified UserId -> [UserId] -> Maybe Text -> Maybe Milliseconds -> @@ -1313,7 +1313,7 @@ assertConvWithRole :: Response (Maybe Lazy.ByteString) -> ConvType -> UserId -> - UserId -> + Qualified UserId -> [UserId] -> Maybe Text -> Maybe Milliseconds -> @@ -2332,7 +2332,14 @@ checkConvCreateEvent cid w = WS.assertMatch_ checkTimeout w $ \notif -> do Conv.EdConversation x -> (qUnqualified . cnvQualifiedId) x @?= cid other -> assertFailure $ "Unexpected event data: " <> show other -wsAssertConvCreateWithRole :: HasCallStack => Qualified ConvId -> Qualified UserId -> UserId -> [(Qualified UserId, RoleName)] -> Notification -> IO () +wsAssertConvCreateWithRole :: + HasCallStack => + Qualified ConvId -> + Qualified UserId -> + Qualified UserId -> + [(Qualified UserId, RoleName)] -> + Notification -> + IO () wsAssertConvCreateWithRole conv eventFrom selfMember otherMembers n = do let e = List1.head (WS.unpackPayload n) ntfTransient n @?= False diff --git a/services/galley/test/unit/Test/Galley/Mapping.hs b/services/galley/test/unit/Test/Galley/Mapping.hs index 940178095f..ffda58da20 100644 --- a/services/galley/test/unit/Test/Galley/Mapping.hs +++ b/services/galley/test/unit/Test/Galley/Mapping.hs @@ -42,31 +42,31 @@ tests = testGroup "ConversationMapping" [ testProperty "conversation view for a valid user is non-empty" $ - \(ConvWithLocalUser c uid) dom -> isJust (conversationViewMaybe dom uid c), + \(ConvWithLocalUser c luid) -> isJust (conversationViewMaybe luid c), testProperty "self user in conversation view is correct" $ - \(ConvWithLocalUser c uid) dom -> - fmap (memId . cmSelf . cnvMembers) (conversationViewMaybe dom uid c) - == Just uid, + \(ConvWithLocalUser c luid) -> + fmap (memId . cmSelf . cnvMembers) (conversationViewMaybe luid c) + == Just (qUntagged luid), testProperty "conversation view metadata is correct" $ - \(ConvWithLocalUser c uid) dom -> - fmap cnvMetadata (conversationViewMaybe dom uid c) + \(ConvWithLocalUser c luid) -> + fmap cnvMetadata (conversationViewMaybe luid c) == Just (Data.convMetadata c), testProperty "other members in conversation view do not contain self" $ - \(ConvWithLocalUser c uid) dom -> case conversationViewMaybe dom uid c of + \(ConvWithLocalUser c luid) -> case conversationViewMaybe luid c of Nothing -> False Just cnv -> not - ( Qualified uid dom + ( qUntagged luid `elem` (map omQualifiedId (cmOthers (cnvMembers cnv))) ), testProperty "conversation view contains all users" $ - \(ConvWithLocalUser c uid) dom -> - fmap (sort . cnvUids dom) (conversationViewMaybe dom uid c) - == Just (sort (convUids dom c)), + \(ConvWithLocalUser c luid) -> + fmap (sort . cnvUids) (conversationViewMaybe luid c) + == Just (sort (convUids (tDomain luid) c)), testProperty "conversation view for an invalid user is empty" $ - \(RandomConversation c) dom uid -> - not (elem uid (map lmId (Data.convLocalMembers c))) - ==> isNothing (conversationViewMaybe dom uid c), + \(RandomConversation c) luid -> + not (elem (tUnqualified luid) (map lmId (Data.convLocalMembers c))) + ==> isNothing (conversationViewMaybe luid c), testProperty "remote conversation view for a valid user is non-empty" $ \(ConvWithRemoteUser c ruid) dom -> qDomain (qUntagged ruid) /= dom @@ -91,10 +91,10 @@ tests = ) ] -cnvUids :: Domain -> Conversation -> [Qualified UserId] -cnvUids dom c = +cnvUids :: Conversation -> [Qualified UserId] +cnvUids c = let mems = cnvMembers c - in Qualified (memId (cmSelf mems)) dom : + in memId (cmSelf mems) : map omQualifiedId (cmOthers mems) convUids :: Domain -> Data.Conversation -> [Qualified UserId] @@ -136,14 +136,16 @@ newtype RandomConversation = RandomConversation instance Arbitrary RandomConversation where arbitrary = RandomConversation <$> genConversation -data ConvWithLocalUser = ConvWithLocalUser Data.Conversation UserId +data ConvWithLocalUser = ConvWithLocalUser Data.Conversation (Local UserId) deriving (Show) instance Arbitrary ConvWithLocalUser where arbitrary = do member <- genLocalMember - ConvWithLocalUser <$> genConv member <*> pure (lmId member) + ConvWithLocalUser <$> genConv member <*> genLocal (lmId member) where + genLocal :: x -> Gen (Local x) + genLocal v = flip toLocalUnsafe v <$> arbitrary genConv m = uniqueMembers m . unRandomConversation <$> arbitrary uniqueMembers :: LocalMember -> Data.Conversation -> Data.Conversation uniqueMembers m c = diff --git a/tools/api-simulations/lib/src/Network/Wire/Simulations.hs b/tools/api-simulations/lib/src/Network/Wire/Simulations.hs index f0f383a491..7a34d8280b 100644 --- a/tools/api-simulations/lib/src/Network/Wire/Simulations.hs +++ b/tools/api-simulations/lib/src/Network/Wire/Simulations.hs @@ -81,7 +81,8 @@ prepareConv (a : bs) = do mapM_ (connectIfNeeded a) bs let bIds = map botId bs conv <- qUnqualified . cnvQualifiedId <$> runBotSession a (createConv bIds Nothing) - assertConvCreated conv a bs + lconv <- qualifyLocal conv + assertConvCreated lconv a bs return conv -- | Make sure that there is a connection between two bots. diff --git a/tools/api-simulations/smoketest/src/Network/Wire/Simulations/SmokeTest.hs b/tools/api-simulations/smoketest/src/Network/Wire/Simulations/SmokeTest.hs index 529cdf2948..4fcd6d0acf 100644 --- a/tools/api-simulations/smoketest/src/Network/Wire/Simulations/SmokeTest.hs +++ b/tools/api-simulations/smoketest/src/Network/Wire/Simulations/SmokeTest.hs @@ -89,7 +89,8 @@ mainBotNet n = do meetup <- runBotSession ally $ do let others = bill : carl : goons conv <- qUnqualified . cnvQualifiedId <$> createConv (map botId others) (Just "Meetup") - assertConvCreated conv ally others + lconv <- qualifyLocal conv + assertConvCreated lconv ally others return conv info $ msg "Bill updates his member state" localDomain <- viewFederationDomain