feat: Allow removing a ratchet session
This commit is contained in:
		
							parent
							
								
									710b3c9497
								
							
						
					
					
						commit
						5a097e4d2a
					
				| @ -12,6 +12,14 @@ class RatchetModifiedEvent extends OmemoEvent { | |||||||
|   final OmemoDoubleRatchet ratchet; |   final OmemoDoubleRatchet ratchet; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /// Triggered when a ratchet has been removed and should be removed from storage. | ||||||
|  | class RatchetRemovedEvent extends OmemoEvent { | ||||||
|  | 
 | ||||||
|  |   RatchetRemovedEvent(this.jid, this.deviceId); | ||||||
|  |   final String jid; | ||||||
|  |   final int deviceId; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /// Triggered when the device map has been modified | /// Triggered when the device map has been modified | ||||||
| class DeviceMapModifiedEvent extends OmemoEvent { | class DeviceMapModifiedEvent extends OmemoEvent { | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -3,7 +3,8 @@ import 'dart:convert'; | |||||||
| import 'package:collection/collection.dart'; | import 'package:collection/collection.dart'; | ||||||
| import 'package:cryptography/cryptography.dart'; | import 'package:cryptography/cryptography.dart'; | ||||||
| import 'package:hex/hex.dart'; | import 'package:hex/hex.dart'; | ||||||
| import 'package:meta/meta.dart'; import 'package:omemo_dart/src/crypto.dart'; | import 'package:meta/meta.dart'; | ||||||
|  | import 'package:omemo_dart/src/crypto.dart'; | ||||||
| import 'package:omemo_dart/src/double_ratchet/double_ratchet.dart'; | import 'package:omemo_dart/src/double_ratchet/double_ratchet.dart'; | ||||||
| import 'package:omemo_dart/src/errors.dart'; | import 'package:omemo_dart/src/errors.dart'; | ||||||
| import 'package:omemo_dart/src/helpers.dart'; | import 'package:omemo_dart/src/helpers.dart'; | ||||||
| @ -93,6 +94,7 @@ class OmemoSessionManager { | |||||||
|   /// A stream that receives events regarding the session |   /// A stream that receives events regarding the session | ||||||
|   Stream<OmemoEvent> get eventStream => _eventStreamController.stream; |   Stream<OmemoEvent> get eventStream => _eventStreamController.stream; | ||||||
| 
 | 
 | ||||||
|  |   /// Returns our own device. | ||||||
|   Future<Device> getDevice() async { |   Future<Device> getDevice() async { | ||||||
|     Device? dev; |     Device? dev; | ||||||
|     await _deviceLock.synchronized(() async { |     await _deviceLock.synchronized(() async { | ||||||
| @ -409,6 +411,26 @@ class OmemoSessionManager { | |||||||
|     return map!; |     return map!; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   /// Removes the ratchet identified by [jid] and [deviceId] from the session manager. | ||||||
|  |   /// Also triggers events for commiting the new device map to storage and removing | ||||||
|  |   /// the old ratchet. | ||||||
|  |   Future<void> removeRatchet(String jid, int deviceId) async { | ||||||
|  |     await _lock.synchronized(() async { | ||||||
|  |       // Remove the ratchet | ||||||
|  |       _ratchetMap.remove(RatchetMapKey(jid, deviceId)); | ||||||
|  |       // Commit it | ||||||
|  |       _eventStreamController.add(RatchetRemovedEvent(jid, deviceId)); | ||||||
|  | 
 | ||||||
|  |       // Remove the device from jid | ||||||
|  |       _deviceMap[jid]!.remove(deviceId); | ||||||
|  |       if (_deviceMap[jid]!.isEmpty) { | ||||||
|  |         _deviceMap.remove(jid); | ||||||
|  |       } | ||||||
|  |       // Commit it | ||||||
|  |       _eventStreamController.add(DeviceMapModifiedEvent(_deviceMap)); | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  |    | ||||||
|   @visibleForTesting |   @visibleForTesting | ||||||
|   OmemoDoubleRatchet getRatchet(String jid, int deviceId) => _ratchetMap[RatchetMapKey(jid, deviceId)]!; |   OmemoDoubleRatchet getRatchet(String jid, int deviceId) => _ratchetMap[RatchetMapKey(jid, deviceId)]!; | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -422,4 +422,81 @@ void main() { | |||||||
|       expect(messageText, aliceReceivedMessage); |       expect(messageText, aliceReceivedMessage); | ||||||
|     } |     } | ||||||
|   }); |   }); | ||||||
|  | 
 | ||||||
|  |   group('Test removing a ratchet', () { | ||||||
|  |     test('Test removing a ratchet when the user has multiple', () async { | ||||||
|  |       const aliceJid = 'alice@server.local'; | ||||||
|  |       const bobJid = 'bob@some.server.local'; | ||||||
|  |       final aliceSession = await OmemoSessionManager.generateNewIdentity( | ||||||
|  |         aliceJid, | ||||||
|  |         AlwaysTrustingTrustManager(), | ||||||
|  |         opkAmount: 1, | ||||||
|  |       ); | ||||||
|  |       final bobSession1 = await OmemoSessionManager.generateNewIdentity( | ||||||
|  |         bobJid, | ||||||
|  |         AlwaysTrustingTrustManager(), | ||||||
|  |         opkAmount: 1, | ||||||
|  |       ); | ||||||
|  |       final bobSession2 = await OmemoSessionManager.generateNewIdentity( | ||||||
|  |         bobJid, | ||||||
|  |         AlwaysTrustingTrustManager(), | ||||||
|  |         opkAmount: 1, | ||||||
|  |       ); | ||||||
|  | 
 | ||||||
|  |       // Alice sends a message to those two Bobs | ||||||
|  |       final aliceMessage = await aliceSession.encryptToJid( | ||||||
|  |         bobJid, | ||||||
|  |         'Hallo Welt', | ||||||
|  |         newSessions: [ | ||||||
|  |           await (await bobSession1.getDevice()).toBundle(), | ||||||
|  |           await (await bobSession2.getDevice()).toBundle(), | ||||||
|  |         ], | ||||||
|  |       ); | ||||||
|  | 
 | ||||||
|  |       // One of those two sessions is broken, so Alice removes the session2 ratchet | ||||||
|  |       final id1 = (await bobSession1.getDevice()).id; | ||||||
|  |       final id2 = (await bobSession2.getDevice()).id; | ||||||
|  |       await aliceSession.removeRatchet(bobJid, id1); | ||||||
|  | 
 | ||||||
|  |       final map = await aliceSession.getRatchetMap(); | ||||||
|  |       expect(map.containsKey(RatchetMapKey(bobJid, id1)), false); | ||||||
|  |       expect(map.containsKey(RatchetMapKey(bobJid, id2)), true); | ||||||
|  |       final deviceMap = await aliceSession.getDeviceMap(); | ||||||
|  |       expect(deviceMap.containsKey(bobJid), true); | ||||||
|  |       expect(deviceMap[bobJid], [id2]); | ||||||
|  |     }); | ||||||
|  | 
 | ||||||
|  |     test('Test removing a ratchet when the user has only one', () async { | ||||||
|  |       const aliceJid = 'alice@server.local'; | ||||||
|  |       const bobJid = 'bob@some.server.local'; | ||||||
|  |       final aliceSession = await OmemoSessionManager.generateNewIdentity( | ||||||
|  |         aliceJid, | ||||||
|  |         AlwaysTrustingTrustManager(), | ||||||
|  |         opkAmount: 1, | ||||||
|  |       ); | ||||||
|  |       final bobSession = await OmemoSessionManager.generateNewIdentity( | ||||||
|  |         bobJid, | ||||||
|  |         AlwaysTrustingTrustManager(), | ||||||
|  |         opkAmount: 1, | ||||||
|  |       ); | ||||||
|  | 
 | ||||||
|  |       // Alice sends a message to those two Bobs | ||||||
|  |       final aliceMessage = await aliceSession.encryptToJid( | ||||||
|  |         bobJid, | ||||||
|  |         'Hallo Welt', | ||||||
|  |         newSessions: [ | ||||||
|  |           await (await bobSession.getDevice()).toBundle(), | ||||||
|  |         ], | ||||||
|  |       ); | ||||||
|  | 
 | ||||||
|  |       // One of those two sessions is broken, so Alice removes the session2 ratchet | ||||||
|  |       final id = (await bobSession.getDevice()).id; | ||||||
|  |       await aliceSession.removeRatchet(bobJid, id); | ||||||
|  | 
 | ||||||
|  |       final map = await aliceSession.getRatchetMap(); | ||||||
|  |       expect(map.containsKey(RatchetMapKey(bobJid, id)), false); | ||||||
|  |       final deviceMap = await aliceSession.getDeviceMap(); | ||||||
|  |       expect(deviceMap.containsKey(bobJid), false); | ||||||
|  |     }); | ||||||
|  |   }); | ||||||
| } | } | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user