feat: ensure direct chats have only 2 members before sending verification requests
This commit is contained in:
parent
64fa9098db
commit
77ec2e0604
|
|
@ -451,7 +451,8 @@ class Client extends MatrixApi {
|
|||
Map<String, dynamic> get directChats =>
|
||||
_accountData['m.direct']?.content ?? {};
|
||||
|
||||
/// Returns the (first) room ID from the store which is a private chat with the user [userId].
|
||||
/// Returns the first room ID from the store (the room with the latest event)
|
||||
/// which is a private chat with the user [userId].
|
||||
/// Returns null if there is none.
|
||||
String? getDirectChatFromUserId(String userId) {
|
||||
final directChats = _accountData['m.direct']?.content[userId];
|
||||
|
|
|
|||
|
|
@ -77,15 +77,42 @@ class DeviceKeysList {
|
|||
}
|
||||
if (userId != client.userID) {
|
||||
// in-room verification with someone else
|
||||
final roomId = await client.startDirectChat(
|
||||
Room? room;
|
||||
// we check if there's already a direct chat with the user
|
||||
for (final directChatRoomId in client.directChats[userId] ?? []) {
|
||||
final tempRoom = client.getRoomById(directChatRoomId);
|
||||
if (tempRoom != null &&
|
||||
// check if the room is a direct chat and has less than 2 members
|
||||
// (including the invited users)
|
||||
(tempRoom.summary.mInvitedMemberCount ?? 0) +
|
||||
(tempRoom.summary.mJoinedMemberCount ?? 1) <=
|
||||
2) {
|
||||
// Now we check if the users in the room are none other than the current
|
||||
// user and the user we want to verify
|
||||
final members = tempRoom.getParticipants([
|
||||
Membership.invite,
|
||||
Membership.join,
|
||||
]);
|
||||
if (members.every((m) => {userId, client.userID}.contains(m.id))) {
|
||||
// if so, we use that room
|
||||
room = tempRoom;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// if there's no direct chat that satisfies the conditions, we create a new one
|
||||
if (room == null) {
|
||||
final newRoomId = await client.startDirectChat(
|
||||
userId,
|
||||
enableEncryption: newDirectChatEnableEncryption,
|
||||
initialState: newDirectChatInitialState,
|
||||
waitForSync: false,
|
||||
skipExistingChat: true, // to create a new room directly
|
||||
);
|
||||
room = client.getRoomById(newRoomId) ??
|
||||
Room(id: newRoomId, client: client);
|
||||
}
|
||||
|
||||
final room =
|
||||
client.getRoomById(roomId) ?? Room(id: roomId, client: client);
|
||||
final request =
|
||||
KeyVerification(encryption: encryption, room: room, userId: userId);
|
||||
await request.start();
|
||||
|
|
|
|||
|
|
@ -1273,6 +1273,23 @@ void main() {
|
|||
});
|
||||
test('startDirectChat', () async {
|
||||
await matrix.startDirectChat('@alice:example.com', waitForSync: false);
|
||||
|
||||
expect(
|
||||
json.decode(
|
||||
FakeMatrixApi.calledEndpoints['/client/v3/createRoom']?.last,
|
||||
),
|
||||
{
|
||||
'initial_state': [
|
||||
{
|
||||
'content': {'algorithm': 'm.megolm.v1.aes-sha2'},
|
||||
'type': 'm.room.encryption',
|
||||
}
|
||||
],
|
||||
'invite': ['@alice:example.com'],
|
||||
'is_direct': true,
|
||||
'preset': 'trusted_private_chat',
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
test('createGroupChat', () async {
|
||||
|
|
|
|||
|
|
@ -275,10 +275,216 @@ void main() {
|
|||
expect(req != null, true);
|
||||
expect(req?.room != null, false);
|
||||
|
||||
final createRoomRequestCount =
|
||||
FakeMatrixApi.calledEndpoints['/client/v3/createRoom']?.length ?? 0;
|
||||
|
||||
Future<void> verifyDeviceKeys() async {
|
||||
req = await client.userDeviceKeys['@alice:example.com']
|
||||
?.startVerification(newDirectChatEnableEncryption: false);
|
||||
expect(req != null, true);
|
||||
expect(req?.room != null, true);
|
||||
}
|
||||
|
||||
await verifyDeviceKeys();
|
||||
// a new room should be created since there is no existing DM room
|
||||
expect(
|
||||
FakeMatrixApi.calledEndpoints['/client/v3/createRoom']?.length,
|
||||
createRoomRequestCount + 1,
|
||||
);
|
||||
|
||||
await verifyDeviceKeys();
|
||||
// no new room should be created since the room already exists
|
||||
expect(
|
||||
FakeMatrixApi.calledEndpoints['/client/v3/createRoom']?.length,
|
||||
createRoomRequestCount + 1,
|
||||
);
|
||||
|
||||
final dmRoomId = client.getDirectChatFromUserId('@alice:example.com');
|
||||
expect(dmRoomId != null, true);
|
||||
final dmRoom = client.getRoomById(dmRoomId!);
|
||||
expect(dmRoom != null, true);
|
||||
// old state event should not overwrite current state events
|
||||
dmRoom!.partial = false;
|
||||
|
||||
// mock invite bob to the room
|
||||
await client.handleSync(
|
||||
SyncUpdate(
|
||||
nextBatch: 'something',
|
||||
rooms: RoomsUpdate(
|
||||
join: {
|
||||
dmRoomId: JoinedRoomUpdate(
|
||||
state: [
|
||||
MatrixEvent(
|
||||
type: EventTypes.RoomMember,
|
||||
content: {
|
||||
'displayname': 'testclient',
|
||||
'is_direct': true,
|
||||
'membership': Membership.join.name,
|
||||
},
|
||||
senderId: client.userID!,
|
||||
eventId: 'eventId',
|
||||
stateKey: client.userID!,
|
||||
originServerTs: DateTime.now(),
|
||||
),
|
||||
MatrixEvent(
|
||||
type: EventTypes.RoomMember,
|
||||
content: {
|
||||
'displayname': 'Bob the builder',
|
||||
'is_direct': true,
|
||||
'membership': Membership.invite.name,
|
||||
},
|
||||
senderId: '@bob:example.com',
|
||||
eventId: 'eventId',
|
||||
stateKey: '@bob:example.com',
|
||||
originServerTs: DateTime.now(),
|
||||
),
|
||||
],
|
||||
summary: RoomSummary.fromJson({
|
||||
'm.joined_member_count': 1,
|
||||
'm.invited_member_count': 1,
|
||||
'm.heroes': [],
|
||||
}),
|
||||
),
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
expect(
|
||||
dmRoom.getParticipants([Membership.invite, Membership.join]).length,
|
||||
2,
|
||||
);
|
||||
dmRoom.partial = true;
|
||||
|
||||
await verifyDeviceKeys();
|
||||
// a second room should now be created because bob(someone else other than
|
||||
// alice) is invited into the first DM room
|
||||
expect(
|
||||
FakeMatrixApi.calledEndpoints['/client/v3/createRoom']?.length,
|
||||
createRoomRequestCount + 2,
|
||||
);
|
||||
|
||||
final dmRoomId2 = client.getDirectChatFromUserId('@alice:example.com');
|
||||
expect(dmRoomId2 != null, true);
|
||||
final dmRoom2 = client.getRoomById(dmRoomId2!);
|
||||
expect(dmRoom2 != null, true);
|
||||
// old state event should not overwrite current state events
|
||||
dmRoom2!.partial = false;
|
||||
|
||||
// mock invite alice and ban bob to the room
|
||||
await client.handleSync(
|
||||
SyncUpdate(
|
||||
nextBatch: 'something',
|
||||
rooms: RoomsUpdate(
|
||||
join: {
|
||||
dmRoomId2: JoinedRoomUpdate(
|
||||
state: [
|
||||
MatrixEvent(
|
||||
type: EventTypes.RoomMember,
|
||||
content: {
|
||||
'displayname': 'Alice Catgirl',
|
||||
'is_direct': true,
|
||||
'membership': Membership.invite.name,
|
||||
},
|
||||
senderId: '@alice:example.com',
|
||||
eventId: 'eventId',
|
||||
stateKey: '@alice:example.com',
|
||||
originServerTs: DateTime.now(),
|
||||
),
|
||||
MatrixEvent(
|
||||
type: EventTypes.RoomMember,
|
||||
content: {
|
||||
'displayname': 'Bob the builder',
|
||||
'is_direct': true,
|
||||
'membership': Membership.ban.name,
|
||||
},
|
||||
senderId: '@bob:example.com',
|
||||
eventId: 'eventId',
|
||||
stateKey: '@bob:example.com',
|
||||
originServerTs: DateTime.now(),
|
||||
),
|
||||
],
|
||||
summary: RoomSummary.fromJson({
|
||||
'm.joined_member_count': 1,
|
||||
'm.invited_member_count': 1,
|
||||
'm.heroes': [],
|
||||
}),
|
||||
),
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
expect(
|
||||
dmRoom2.getParticipants([Membership.invite, Membership.join]).length,
|
||||
2,
|
||||
);
|
||||
dmRoom2.partial = true;
|
||||
|
||||
await verifyDeviceKeys();
|
||||
// no new room should be created because only alice has been invited to the
|
||||
// second room
|
||||
expect(
|
||||
FakeMatrixApi.calledEndpoints['/client/v3/createRoom']?.length,
|
||||
createRoomRequestCount + 2,
|
||||
);
|
||||
|
||||
// old state event should not overwrite current state events
|
||||
dmRoom2.partial = false;
|
||||
// mock join alice and invite bob to the room
|
||||
await client.handleSync(
|
||||
SyncUpdate(
|
||||
nextBatch: 'something',
|
||||
rooms: RoomsUpdate(
|
||||
join: {
|
||||
dmRoomId2: JoinedRoomUpdate(
|
||||
state: [
|
||||
MatrixEvent(
|
||||
type: EventTypes.RoomMember,
|
||||
content: {
|
||||
'displayname': 'Alice Catgirl',
|
||||
'is_direct': true,
|
||||
'membership': Membership.join.name,
|
||||
},
|
||||
senderId: '@alice:example.com',
|
||||
eventId: 'eventId',
|
||||
stateKey: '@alice:example.com',
|
||||
originServerTs: DateTime.now(),
|
||||
),
|
||||
MatrixEvent(
|
||||
type: EventTypes.RoomMember,
|
||||
content: {
|
||||
'displayname': 'Bob the builder',
|
||||
'is_direct': true,
|
||||
'membership': Membership.invite.name,
|
||||
},
|
||||
senderId: '@bob:example.com',
|
||||
eventId: 'eventId',
|
||||
stateKey: '@bob:example.com',
|
||||
originServerTs: DateTime.now(),
|
||||
),
|
||||
],
|
||||
summary: RoomSummary.fromJson({
|
||||
'm.joined_member_count': 2,
|
||||
'm.invited_member_count': 1,
|
||||
'm.heroes': [],
|
||||
}),
|
||||
),
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
expect(
|
||||
dmRoom.getParticipants([Membership.invite, Membership.join]).length,
|
||||
3,
|
||||
);
|
||||
dmRoom2.partial = true;
|
||||
|
||||
await verifyDeviceKeys();
|
||||
// a third room should now be created because someone else (other than
|
||||
// alice) is also invited into the second DM room
|
||||
expect(
|
||||
FakeMatrixApi.calledEndpoints['/client/v3/createRoom']?.length,
|
||||
createRoomRequestCount + 3,
|
||||
);
|
||||
});
|
||||
|
||||
test('dispose client', () async {
|
||||
|
|
|
|||
Loading…
Reference in New Issue