Merge pull request #359 from loki-project/closed-group-editing

Fix Adding & Removing Members Simultaneously
This commit is contained in:
Niels Andriesse 2020-09-29 13:40:30 +10:00 committed by GitHub
commit 828acc1620
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

View File

@ -112,6 +112,7 @@ object ClosedGroupsProtocol {
return return
} }
val oldMembers = group.members.map { it.serialize() }.toSet() val oldMembers = group.members.map { it.serialize() }.toSet()
val newMembers = members.minus(oldMembers)
val membersAsData = members.map { Hex.fromStringCondensed(it) } val membersAsData = members.map { Hex.fromStringCondensed(it) }
val admins = group.admins.map { it.serialize() } val admins = group.admins.map { it.serialize() }
val adminsAsData = admins.map { Hex.fromStringCondensed(it) } val adminsAsData = admins.map { Hex.fromStringCondensed(it) }
@ -123,6 +124,7 @@ object ClosedGroupsProtocol {
val wasAnyUserRemoved = members.toSet().intersect(oldMembers) != oldMembers.toSet() val wasAnyUserRemoved = members.toSet().intersect(oldMembers) != oldMembers.toSet()
val removedMembers = oldMembers.minus(members) val removedMembers = oldMembers.minus(members)
val isUserLeaving = removedMembers.contains(userPublicKey) val isUserLeaving = removedMembers.contains(userPublicKey)
var newSenderKeys = listOf<ClosedGroupSenderKey>()
if (wasAnyUserRemoved) { if (wasAnyUserRemoved) {
if (isUserLeaving && removedMembers.count() != 1) { if (isUserLeaving && removedMembers.count() != 1) {
Log.d("Loki", "Can't remove self and others simultaneously.") Log.d("Loki", "Can't remove self and others simultaneously.")
@ -161,8 +163,7 @@ object ClosedGroupsProtocol {
} }
} else { } else {
// Generate ratchets for any new members // Generate ratchets for any new members
val newMembers = members.minus(oldMembers) newSenderKeys = newMembers.map { publicKey ->
val newSenderKeys: List<ClosedGroupSenderKey> = newMembers.map { publicKey ->
val ratchet = SharedSenderKeysImplementation.shared.generateRatchet(groupPublicKey, publicKey) val ratchet = SharedSenderKeysImplementation.shared.generateRatchet(groupPublicKey, publicKey)
ClosedGroupSenderKey(Hex.fromStringCondensed(ratchet.chainKey), ratchet.keyIndex, Hex.fromStringCondensed(publicKey)) ClosedGroupSenderKey(Hex.fromStringCondensed(ratchet.chainKey), ratchet.keyIndex, Hex.fromStringCondensed(publicKey))
} }
@ -171,6 +172,7 @@ object ClosedGroupsProtocol {
newSenderKeys, membersAsData, adminsAsData) newSenderKeys, membersAsData, adminsAsData)
val job = ClosedGroupUpdateMessageSendJob(groupPublicKey, closedGroupUpdateKind) val job = ClosedGroupUpdateMessageSendJob(groupPublicKey, closedGroupUpdateKind)
ApplicationContext.getInstance(context).jobManager.add(job) ApplicationContext.getInstance(context).jobManager.add(job)
}
// Establish sessions if needed // Establish sessions if needed
establishSessionsWithMembersIfNeeded(context, newMembers) establishSessionsWithMembersIfNeeded(context, newMembers)
// Send closed group update messages to the new members using established channels // Send closed group update messages to the new members using established channels
@ -183,7 +185,6 @@ object ClosedGroupsProtocol {
val job = ClosedGroupUpdateMessageSendJob(member, closedGroupUpdateKind) val job = ClosedGroupUpdateMessageSendJob(member, closedGroupUpdateKind)
ApplicationContext.getInstance(context).jobManager.add(job) ApplicationContext.getInstance(context).jobManager.add(job)
} }
}
// Update the group // Update the group
groupDB.updateTitle(groupID, name) groupDB.updateTitle(groupID, name)
if (!isUserLeaving) { if (!isUserLeaving) {
@ -225,7 +226,7 @@ object ClosedGroupsProtocol {
when (closedGroupUpdate.type) { when (closedGroupUpdate.type) {
SignalServiceProtos.ClosedGroupUpdate.Type.NEW -> { SignalServiceProtos.ClosedGroupUpdate.Type.NEW -> {
return !closedGroupUpdate.name.isNullOrEmpty() && !(closedGroupUpdate.groupPrivateKey ?: ByteString.copyFrom(ByteArray(0))).isEmpty return !closedGroupUpdate.name.isNullOrEmpty() && !(closedGroupUpdate.groupPrivateKey ?: ByteString.copyFrom(ByteArray(0))).isEmpty
&& closedGroupUpdate.senderKeysCount > 0 && closedGroupUpdate.membersCount > 0 && closedGroupUpdate.adminsCount > 0 && closedGroupUpdate.membersCount > 0 && closedGroupUpdate.adminsCount > 0 // senderKeys may be empty
} }
SignalServiceProtos.ClosedGroupUpdate.Type.INFO -> { SignalServiceProtos.ClosedGroupUpdate.Type.INFO -> {
return !closedGroupUpdate.name.isNullOrEmpty() && closedGroupUpdate.membersCount > 0 && closedGroupUpdate.adminsCount > 0 // senderKeys may be empty return !closedGroupUpdate.name.isNullOrEmpty() && closedGroupUpdate.membersCount > 0 && closedGroupUpdate.adminsCount > 0 // senderKeys may be empty
@ -255,6 +256,24 @@ object ClosedGroupsProtocol {
val ratchet = ClosedGroupRatchet(senderKey.chainKey.toHexString(), senderKey.keyIndex, listOf()) val ratchet = ClosedGroupRatchet(senderKey.chainKey.toHexString(), senderKey.keyIndex, listOf())
sskDatabase.setClosedGroupRatchet(groupPublicKey, senderKey.publicKey.toHexString(), ratchet) sskDatabase.setClosedGroupRatchet(groupPublicKey, senderKey.publicKey.toHexString(), ratchet)
} }
// Sort out any discrepancies between the provided sender keys and what's required
val missingSenderKeys = members.toSet().subtract(senderKeys.map { Hex.toStringCondensed(it.publicKey) })
if (missingSenderKeys.contains(userPublicKey)) {
establishSessionsWithMembersIfNeeded(context, missingSenderKeys)
val userRatchet = SharedSenderKeysImplementation.shared.generateRatchet(groupPublicKey, userPublicKey)
val userSenderKey = ClosedGroupSenderKey(Hex.fromStringCondensed(userRatchet.chainKey), userRatchet.keyIndex, Hex.fromStringCondensed(userPublicKey))
for (member in members) {
if (member == userPublicKey) { continue }
@Suppress("NAME_SHADOWING")
val closedGroupUpdateKind = ClosedGroupUpdateMessageSendJob.Kind.SenderKey(Hex.fromStringCondensed(groupPublicKey), userSenderKey)
@Suppress("NAME_SHADOWING")
val job = ClosedGroupUpdateMessageSendJob(member, closedGroupUpdateKind)
ApplicationContext.getInstance(context).jobManager.add(job)
}
}
for (publicKey in missingSenderKeys.minus(userPublicKey)) {
requestSenderKey(context, groupPublicKey, publicKey)
}
// Create the group // Create the group
val groupID = doubleEncodeGroupID(groupPublicKey) val groupID = doubleEncodeGroupID(groupPublicKey)
DatabaseFactory.getGroupDatabase(context).create(groupID, name, LinkedList<Address>(members.map { Address.fromSerialized(it) }), DatabaseFactory.getGroupDatabase(context).create(groupID, name, LinkedList<Address>(members.map { Address.fromSerialized(it) }),