1
1
use std:: {
2
+ future:: IntoFuture ,
2
3
sync:: { Arc , Mutex } ,
3
4
time:: Duration ,
4
5
} ;
@@ -9,7 +10,7 @@ use assert_matches2::assert_let;
9
10
use assign:: assign;
10
11
use matrix_sdk:: {
11
12
assert_next_eq_with_timeout,
12
- crypto:: { format_emojis, SasState } ,
13
+ crypto:: { format_emojis, SasState , UserDevices } ,
13
14
encryption:: {
14
15
backups:: BackupState ,
15
16
recovery:: { Recovery , RecoveryState } ,
@@ -19,17 +20,21 @@ use matrix_sdk::{
19
20
BackupDownloadStrategy , EncryptionSettings , LocalTrust ,
20
21
} ,
21
22
ruma:: {
22
- api:: client:: room:: create_room:: v3:: Request as CreateRoomRequest ,
23
+ api:: client:: {
24
+ message:: send_message_event,
25
+ room:: create_room:: v3:: { Request as CreateRoomRequest , RoomPreset } ,
26
+ } ,
23
27
events:: {
24
28
key:: verification:: { request:: ToDeviceKeyVerificationRequestEvent , VerificationMethod } ,
25
29
room:: message:: {
26
30
MessageType , OriginalSyncRoomMessageEvent , RoomMessageEventContent ,
27
31
SyncRoomMessageEvent ,
28
32
} ,
29
33
secret_storage:: secret:: SecretEventContent ,
30
- GlobalAccountDataEventType , OriginalSyncMessageLikeEvent ,
34
+ GlobalAccountDataEventType , MessageLikeEventType , OriginalSyncMessageLikeEvent ,
31
35
} ,
32
- OwnedEventId ,
36
+ serde:: Raw ,
37
+ OwnedEventId , TransactionId , UserId ,
33
38
} ,
34
39
timeout:: timeout,
35
40
Client ,
@@ -39,7 +44,7 @@ use matrix_sdk_ui::{
39
44
sync_service:: SyncService ,
40
45
} ;
41
46
use similar_asserts:: assert_eq;
42
- use tracing:: { debug, warn} ;
47
+ use tracing:: { debug, info , warn, Instrument } ;
43
48
44
49
use crate :: helpers:: { SyncTokenAwareClient , TestClientBuilder } ;
45
50
@@ -1175,3 +1180,129 @@ async fn test_recovery_disabling_deletes_secret_storage_secrets() -> Result<()>
1175
1180
1176
1181
Ok ( ( ) )
1177
1182
}
1183
+
1184
+ /// When we invite another user to a room with "joined" history visibility, we
1185
+ /// share the encryption history.
1186
+ #[ tokio:: test( flavor = "multi_thread" , worker_threads = 4 ) ]
1187
+ async fn test_history_share_on_invite ( ) -> Result < ( ) > {
1188
+ let alice_span = tracing:: info_span!( "alice" ) ;
1189
+ let bob_span = tracing:: info_span!( "bob" ) ;
1190
+
1191
+ let encryption_settings =
1192
+ EncryptionSettings { auto_enable_cross_signing : true , ..Default :: default ( ) } ;
1193
+
1194
+ let alice = TestClientBuilder :: new ( "alice" )
1195
+ . use_sqlite ( )
1196
+ . encryption_settings ( encryption_settings)
1197
+ . build ( )
1198
+ . await ?;
1199
+
1200
+ let sync_service_span = tracing:: info_span!( parent: & alice_span, "sync_service" ) ;
1201
+ let alice_sync_service = Arc :: new (
1202
+ SyncService :: builder ( alice. clone ( ) )
1203
+ . with_parent_span ( sync_service_span)
1204
+ . build ( )
1205
+ . await
1206
+ . expect ( "Could not build alice sync service" ) ,
1207
+ ) ;
1208
+
1209
+ alice. encryption ( ) . wait_for_e2ee_initialization_tasks ( ) . await ;
1210
+ alice_sync_service. start ( ) . await ;
1211
+
1212
+ let bob = SyncTokenAwareClient :: new (
1213
+ TestClientBuilder :: new ( "bob" ) . encryption_settings ( encryption_settings) . build ( ) . await ?,
1214
+ ) ;
1215
+
1216
+ {
1217
+ // Alice and Bob share an encrypted room
1218
+ // TODO: get rid of all of this: history sharing should work even if Bob and
1219
+ // Alice do not share a room
1220
+ let alice_shared_room = alice
1221
+ . create_room ( assign ! ( CreateRoomRequest :: new( ) , { preset: Some ( RoomPreset :: PublicChat ) } ) )
1222
+ . await ?;
1223
+ let shared_room_id = alice_shared_room. room_id ( ) ;
1224
+ alice_shared_room. enable_encryption ( ) . await ?;
1225
+ bob. join_room_by_id ( shared_room_id)
1226
+ . instrument ( bob_span. clone ( ) )
1227
+ . await
1228
+ . expect ( "Bob should have joined the room" ) ;
1229
+
1230
+ // Bob sends a message to trigger another sync from Alice, which causes her to
1231
+ // send out the outgoing requests
1232
+ //
1233
+ // FIXME: this appears to be needed due to a bug in the sliding sync client,
1234
+ // which means it does not send out outgoing requests caused by a
1235
+ // /sync response
1236
+ let request = send_message_event:: v3:: Request :: new_raw (
1237
+ shared_room_id. to_owned ( ) ,
1238
+ TransactionId :: new ( ) ,
1239
+ MessageLikeEventType :: Message ,
1240
+ Raw :: new ( & RoomMessageEventContent :: text_plain ( "" ) ) . unwrap ( ) . cast ( ) ,
1241
+ ) ;
1242
+ bob. send ( request) . into_future ( ) . instrument ( bob_span. clone ( ) ) . await ?;
1243
+
1244
+ // Sanity check: Both users see the others' device
1245
+ async fn devices_seen ( client : & Client , other : & UserId ) -> UserDevices {
1246
+ client
1247
+ . olm_machine_for_testing ( )
1248
+ . await
1249
+ . as_ref ( )
1250
+ . unwrap ( )
1251
+ . get_user_devices ( other, Some ( Duration :: from_secs ( 1 ) ) )
1252
+ . await
1253
+ . unwrap ( )
1254
+ }
1255
+
1256
+ timeout (
1257
+ async {
1258
+ loop {
1259
+ let bob_devices = devices_seen ( & alice, bob. user_id ( ) . unwrap ( ) ) . await ;
1260
+ if bob_devices. devices ( ) . count ( ) >= 1 {
1261
+ return ;
1262
+ }
1263
+ }
1264
+ } ,
1265
+ Duration :: from_secs ( 30 ) , // This can take quite a while to happen on the CI runners.
1266
+ )
1267
+ . await
1268
+ . expect ( "Alice did not see bob's device" ) ;
1269
+
1270
+ bob. sync_once ( ) . instrument ( bob_span. clone ( ) ) . await ?;
1271
+ let alice_devices = devices_seen ( & bob, alice. user_id ( ) . unwrap ( ) ) . await ;
1272
+ assert_eq ! ( alice_devices. devices( ) . count( ) , 1 , "Bob did not see Alice's device" ) ;
1273
+ }
1274
+
1275
+ // Alice creates a room ...
1276
+ let alice_room = alice
1277
+ . create_room ( assign ! ( CreateRoomRequest :: new( ) , {
1278
+ preset: Some ( RoomPreset :: PublicChat ) ,
1279
+ } ) )
1280
+ . await ?;
1281
+ alice_room. enable_encryption ( ) . await ?;
1282
+
1283
+ info ! ( room_id = ?alice_room. room_id( ) , "Alice has created and enabled encryption in the room" ) ;
1284
+
1285
+ // ... and sends a message
1286
+ alice_room
1287
+ . send ( RoomMessageEventContent :: text_plain ( "Hello Bob" ) )
1288
+ . await
1289
+ . expect ( "We should be able to send a message to the room" ) ;
1290
+
1291
+ // Alice invites Bob to the room
1292
+ // TODO: invite Bob rather than just call `share_history`
1293
+ alice_room. share_history ( bob. user_id ( ) . unwrap ( ) ) . await ?;
1294
+
1295
+ let bob_response = bob. sync_once ( ) . instrument ( bob_span. clone ( ) ) . await ?;
1296
+
1297
+ // Bob should have received a to-device event with the payload
1298
+ assert_eq ! ( bob_response. to_device. len( ) , 1 ) ;
1299
+ let to_device_event = & bob_response. to_device [ 0 ] ;
1300
+ assert_eq ! (
1301
+ to_device_event. get_field:: <String >( "type" ) . unwrap( ) . unwrap( ) ,
1302
+ "io.element.msc4268.room_key_bundle"
1303
+ ) ;
1304
+
1305
+ // TODO: ensure Bob can decrypt the content
1306
+
1307
+ Ok ( ( ) )
1308
+ }
0 commit comments