mirror of
https://github.com/signalapp/Signal-Android.git
synced 2025-12-24 13:08:46 +00:00
Enforce limit for total number of blocked requests.
This commit is contained in:
committed by
Greyson Parrelli
parent
b3d9a85fa2
commit
6890973ce8
@@ -146,12 +146,13 @@ public class SignalServiceAccountManager {
|
||||
int deviceId,
|
||||
String password,
|
||||
String signalAgent,
|
||||
boolean automaticNetworkRetry)
|
||||
boolean automaticNetworkRetry,
|
||||
int maxGroupSize)
|
||||
{
|
||||
this(configuration,
|
||||
new StaticCredentialsProvider(aci, pni, e164, deviceId, password),
|
||||
signalAgent,
|
||||
new GroupsV2Operations(ClientZkOperations.create(configuration)),
|
||||
new GroupsV2Operations(ClientZkOperations.create(configuration), maxGroupSize),
|
||||
automaticNetworkRetry);
|
||||
}
|
||||
|
||||
|
||||
@@ -108,9 +108,10 @@ public final class GroupChangeReconstruct {
|
||||
builder.addNewPendingMembers(invitedMember);
|
||||
}
|
||||
|
||||
Set<ByteString> consistentMemberUuids = intersect(fromStateMemberUuids, toStateMemberUuids);
|
||||
Set<DecryptedMember> changedMembers = intersectByUUID(subtract(toState.getMembersList(), fromState.getMembersList()), consistentMemberUuids);
|
||||
Map<ByteString, DecryptedMember> membersUuidMap = uuidMap(fromState.getMembersList());
|
||||
Set<ByteString> consistentMemberUuids = intersect(fromStateMemberUuids, toStateMemberUuids);
|
||||
Set<DecryptedMember> changedMembers = intersectByUUID(subtract(toState.getMembersList(), fromState.getMembersList()), consistentMemberUuids);
|
||||
Map<ByteString, DecryptedMember> membersUuidMap = uuidMap(fromState.getMembersList());
|
||||
Map<ByteString, DecryptedBannedMember> bannedMembersUuidMap = bannedUuidMap(toState.getBannedMembersList());
|
||||
|
||||
for (DecryptedMember newState : changedMembers) {
|
||||
DecryptedMember oldState = membersUuidMap.get(newState.getUuid());
|
||||
@@ -152,7 +153,13 @@ public final class GroupChangeReconstruct {
|
||||
}
|
||||
|
||||
for (ByteString uuid : newBannedMemberUuids) {
|
||||
builder.addNewBannedMembers(DecryptedBannedMember.newBuilder().setUuid(uuid).build());
|
||||
DecryptedBannedMember.Builder newBannedBuilder = DecryptedBannedMember.newBuilder().setUuid(uuid);
|
||||
DecryptedBannedMember bannedMember = bannedMembersUuidMap.get(uuid);
|
||||
if (bannedMember != null) {
|
||||
newBannedBuilder.setTimestamp(bannedMember.getTimestamp());
|
||||
}
|
||||
|
||||
builder.addNewBannedMembers(newBannedBuilder);
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
@@ -166,6 +173,14 @@ public final class GroupChangeReconstruct {
|
||||
return map;
|
||||
}
|
||||
|
||||
private static Map<ByteString, DecryptedBannedMember> bannedUuidMap(List<DecryptedBannedMember> membersList) {
|
||||
Map<ByteString, DecryptedBannedMember> map = new LinkedHashMap<>(membersList.size());
|
||||
for (DecryptedBannedMember member : membersList) {
|
||||
map.put(member.getUuid(), member);
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
private static Set<DecryptedMember> intersectByUUID(Collection<DecryptedMember> members, Set<ByteString> uuids) {
|
||||
Set<DecryptedMember> result = new LinkedHashSet<>(members.size());
|
||||
for (DecryptedMember member : members) {
|
||||
|
||||
@@ -44,6 +44,7 @@ import org.whispersystems.signalservice.api.util.UuidUtil;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Optional;
|
||||
@@ -67,12 +68,14 @@ public final class GroupsV2Operations {
|
||||
private final ServerPublicParams serverPublicParams;
|
||||
private final ClientZkProfileOperations clientZkProfileOperations;
|
||||
private final ClientZkAuthOperations clientZkAuthOperations;
|
||||
private final int maxGroupSize;
|
||||
private final SecureRandom random;
|
||||
|
||||
public GroupsV2Operations(ClientZkOperations clientZkOperations) {
|
||||
public GroupsV2Operations(ClientZkOperations clientZkOperations, int maxGroupSize) {
|
||||
this.serverPublicParams = clientZkOperations.getServerPublicParams();
|
||||
this.clientZkProfileOperations = clientZkOperations.getProfileOperations();
|
||||
this.clientZkAuthOperations = clientZkOperations.getAuthOperations();
|
||||
this.maxGroupSize = maxGroupSize;
|
||||
this.random = new SecureRandom();
|
||||
}
|
||||
|
||||
@@ -209,8 +212,8 @@ public final class GroupsV2Operations {
|
||||
return actions;
|
||||
}
|
||||
|
||||
public GroupChange.Actions.Builder createRefuseGroupJoinRequest(Set<UUID> requestsToRemove, boolean alsoBan) {
|
||||
GroupChange.Actions.Builder actions = alsoBan ? createBanUuidsChange(requestsToRemove, false)
|
||||
public GroupChange.Actions.Builder createRefuseGroupJoinRequest(Set<UUID> requestsToRemove, boolean alsoBan, List<DecryptedBannedMember> bannedMembers) {
|
||||
GroupChange.Actions.Builder actions = alsoBan ? createBanUuidsChange(requestsToRemove, false, bannedMembers)
|
||||
: GroupChange.Actions.newBuilder();
|
||||
|
||||
for (UUID uuid : requestsToRemove) {
|
||||
@@ -235,8 +238,8 @@ public final class GroupsV2Operations {
|
||||
return actions;
|
||||
}
|
||||
|
||||
public GroupChange.Actions.Builder createRemoveMembersChange(final Set<UUID> membersToRemove, boolean alsoBan) {
|
||||
GroupChange.Actions.Builder actions = alsoBan ? createBanUuidsChange(membersToRemove, false)
|
||||
public GroupChange.Actions.Builder createRemoveMembersChange(final Set<UUID> membersToRemove, boolean alsoBan, List<DecryptedBannedMember> bannedMembers) {
|
||||
GroupChange.Actions.Builder actions = alsoBan ? createBanUuidsChange(membersToRemove, false, bannedMembers)
|
||||
: GroupChange.Actions.newBuilder();
|
||||
|
||||
for (UUID remove: membersToRemove) {
|
||||
@@ -249,7 +252,7 @@ public final class GroupsV2Operations {
|
||||
}
|
||||
|
||||
public GroupChange.Actions.Builder createLeaveAndPromoteMembersToAdmin(UUID self, List<UUID> membersToMakeAdmin) {
|
||||
GroupChange.Actions.Builder actions = createRemoveMembersChange(Collections.singleton(self), false);
|
||||
GroupChange.Actions.Builder actions = createRemoveMembersChange(Collections.singleton(self), false, Collections.emptyList());
|
||||
|
||||
for (UUID member : membersToMakeAdmin) {
|
||||
actions.addModifyMemberRoles(GroupChange.Actions.ModifyMemberRoleAction
|
||||
@@ -350,10 +353,23 @@ public final class GroupsV2Operations {
|
||||
.setAnnouncementsOnly(isAnnouncementGroup));
|
||||
}
|
||||
|
||||
public GroupChange.Actions.Builder createBanUuidsChange(Set<UUID> banUuids, boolean rejectJoinRequest) {
|
||||
GroupChange.Actions.Builder builder = rejectJoinRequest ? createRefuseGroupJoinRequest(banUuids, false)
|
||||
public GroupChange.Actions.Builder createBanUuidsChange(Set<UUID> banUuids, boolean rejectJoinRequest, List<DecryptedBannedMember> bannedMembersList) {
|
||||
GroupChange.Actions.Builder builder = rejectJoinRequest ? createRefuseGroupJoinRequest(banUuids, false, Collections.emptyList())
|
||||
: GroupChange.Actions.newBuilder();
|
||||
|
||||
int spacesToFree = bannedMembersList.size() + banUuids.size() - maxGroupSize;
|
||||
if (spacesToFree > 0) {
|
||||
List<ByteString> unban = bannedMembersList.stream()
|
||||
.sorted(Comparator.comparingLong(DecryptedBannedMember::getTimestamp))
|
||||
.limit(spacesToFree)
|
||||
.map(DecryptedBannedMember::getUuid)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
for (ByteString uuid : unban) {
|
||||
builder.addDeleteBannedMembers(GroupChange.Actions.DeleteBannedMemberAction.newBuilder().setDeletedUserId(encryptUuid(UuidUtil.fromByteString(uuid))));
|
||||
}
|
||||
}
|
||||
|
||||
for (UUID uuid : banUuids) {
|
||||
builder.addAddBannedMembers(GroupChange.Actions.AddBannedMemberAction.newBuilder().setAdded(BannedMember.newBuilder().setUserId(encryptUuid(uuid)).build()));
|
||||
}
|
||||
@@ -458,7 +474,7 @@ public final class GroupsV2Operations {
|
||||
}
|
||||
|
||||
for (BannedMember member : group.getBannedMembersList()) {
|
||||
decryptedBannedMembers.add(DecryptedBannedMember.newBuilder().setUuid(decryptUuidToByteString(member.getUserId())).build());
|
||||
decryptedBannedMembers.add(DecryptedBannedMember.newBuilder().setUuid(decryptUuidToByteString(member.getUserId())).setTimestamp(member.getTimestamp()).build());
|
||||
}
|
||||
|
||||
return DecryptedGroup.newBuilder()
|
||||
@@ -662,7 +678,7 @@ public final class GroupsV2Operations {
|
||||
|
||||
// Field 22
|
||||
for (GroupChange.Actions.AddBannedMemberAction action : actions.getAddBannedMembersList()) {
|
||||
builder.addNewBannedMembers(DecryptedBannedMember.newBuilder().setUuid(decryptUuidToByteString(action.getAdded().getUserId())).build());
|
||||
builder.addNewBannedMembers(DecryptedBannedMember.newBuilder().setUuid(decryptUuidToByteString(action.getAdded().getUserId())).setTimestamp(action.getAdded().getTimestamp()).build());
|
||||
}
|
||||
|
||||
// Field 23
|
||||
|
||||
@@ -34,7 +34,8 @@ message DecryptedRequestingMember {
|
||||
}
|
||||
|
||||
message DecryptedBannedMember {
|
||||
bytes uuid = 1;
|
||||
bytes uuid = 1;
|
||||
uint64 timestamp = 2;
|
||||
}
|
||||
|
||||
message DecryptedPendingMemberRemoval {
|
||||
|
||||
@@ -46,7 +46,8 @@ message RequestingMember {
|
||||
}
|
||||
|
||||
message BannedMember {
|
||||
bytes userId = 1;
|
||||
bytes userId = 1;
|
||||
uint64 timestamp = 2;
|
||||
}
|
||||
|
||||
message AccessControl {
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
package org.whispersystems.signalservice.api.groupsv2;
|
||||
|
||||
import com.google.protobuf.ByteString;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.signal.storageservice.protos.groups.BannedMember;
|
||||
import org.signal.storageservice.protos.groups.GroupChange;
|
||||
import org.signal.storageservice.protos.groups.GroupChange.Actions.AddBannedMemberAction;
|
||||
import org.signal.storageservice.protos.groups.GroupChange.Actions.DeleteBannedMemberAction;
|
||||
import org.signal.storageservice.protos.groups.local.DecryptedBannedMember;
|
||||
import org.signal.zkgroup.InvalidInputException;
|
||||
import org.signal.zkgroup.groups.GroupMasterKey;
|
||||
import org.signal.zkgroup.groups.GroupSecretParams;
|
||||
import org.whispersystems.signalservice.api.util.UuidUtil;
|
||||
import org.whispersystems.signalservice.internal.util.Util;
|
||||
import org.whispersystems.signalservice.testutil.LibSignalLibraryUtil;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.hasItems;
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.whispersystems.signalservice.api.groupsv2.ProtoTestUtils.bannedMember;
|
||||
|
||||
public final class GroupsV2Operations_ban_Test {
|
||||
|
||||
private GroupsV2Operations.GroupOperations groupOperations;
|
||||
|
||||
@Before
|
||||
public void setup() throws InvalidInputException {
|
||||
LibSignalLibraryUtil.assumeLibSignalSupportedOnOS();
|
||||
|
||||
TestZkGroupServer server = new TestZkGroupServer();
|
||||
GroupSecretParams groupSecretParams = GroupSecretParams.deriveFromMasterKey(new GroupMasterKey(Util.getSecretBytes(32)));
|
||||
ClientZkOperations clientZkOperations = new ClientZkOperations(server.getServerPublicParams());
|
||||
|
||||
groupOperations = new GroupsV2Operations(clientZkOperations, 10).forGroup(groupSecretParams);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addBanToEmptyList() {
|
||||
UUID ban = UUID.randomUUID();
|
||||
|
||||
GroupChange.Actions.Builder banUuidsChange = groupOperations.createBanUuidsChange(Collections.singleton(ban),
|
||||
false,
|
||||
Collections.emptyList());
|
||||
|
||||
assertThat(banUuidsChange.getAddBannedMembersCount(), is(1));
|
||||
assertThat(banUuidsChange.getAddBannedMembers(0).getAdded().getUserId(), is(groupOperations.encryptUuid(ban)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addBanToPartialFullList() {
|
||||
UUID toBan = UUID.randomUUID();
|
||||
List<DecryptedBannedMember> alreadyBanned = new ArrayList<>(5);
|
||||
|
||||
for (int i = 0; i < 5; i++) {
|
||||
alreadyBanned.add(bannedMember(UUID.randomUUID()));
|
||||
}
|
||||
|
||||
GroupChange.Actions.Builder banUuidsChange = groupOperations.createBanUuidsChange(Collections.singleton(toBan),
|
||||
false,
|
||||
alreadyBanned);
|
||||
|
||||
assertThat(banUuidsChange.getAddBannedMembersCount(), is(1));
|
||||
assertThat(banUuidsChange.getAddBannedMembers(0).getAdded().getUserId(), is(groupOperations.encryptUuid(toBan)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addBanToFullList() {
|
||||
UUID toBan = UUID.randomUUID();
|
||||
List<DecryptedBannedMember> alreadyBanned = new ArrayList<>(10);
|
||||
DecryptedBannedMember oldest = null;
|
||||
|
||||
for (int i = 0; i < 10; i++) {
|
||||
DecryptedBannedMember member = bannedMember(UUID.randomUUID()).toBuilder().setTimestamp(100 + i).build();
|
||||
if (oldest == null) {
|
||||
oldest = member;
|
||||
}
|
||||
alreadyBanned.add(member);
|
||||
}
|
||||
|
||||
Collections.shuffle(alreadyBanned);
|
||||
|
||||
GroupChange.Actions.Builder banUuidsChange = groupOperations.createBanUuidsChange(Collections.singleton(toBan),
|
||||
false,
|
||||
alreadyBanned);
|
||||
|
||||
assertThat(banUuidsChange.getDeleteBannedMembersCount(), is(1));
|
||||
assertThat(banUuidsChange.getDeleteBannedMembers(0).getDeletedUserId(), is(groupOperations.encryptUuid(UuidUtil.fromByteString(oldest.getUuid()))));
|
||||
|
||||
|
||||
assertThat(banUuidsChange.getAddBannedMembersCount(), is(1));
|
||||
assertThat(banUuidsChange.getAddBannedMembers(0).getAdded().getUserId(), is(groupOperations.encryptUuid(toBan)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void addMultipleBanToFullList() {
|
||||
List<UUID> toBan = new ArrayList<>();
|
||||
toBan.add(UUID.randomUUID());
|
||||
toBan.add(UUID.randomUUID());
|
||||
|
||||
List<DecryptedBannedMember> alreadyBanned = new ArrayList<>(10);
|
||||
for (int i = 0; i < 10; i++) {
|
||||
alreadyBanned.add(bannedMember(UUID.randomUUID()).toBuilder().setTimestamp(100 + i).build());
|
||||
}
|
||||
|
||||
List<ByteString> oldest = new ArrayList<>(2);
|
||||
oldest.add(groupOperations.encryptUuid(UuidUtil.fromByteString(alreadyBanned.get(0).getUuid())));
|
||||
oldest.add(groupOperations.encryptUuid(UuidUtil.fromByteString(alreadyBanned.get(1).getUuid())));
|
||||
|
||||
Collections.shuffle(alreadyBanned);
|
||||
|
||||
GroupChange.Actions.Builder banUuidsChange = groupOperations.createBanUuidsChange(new HashSet<>(toBan),
|
||||
false,
|
||||
alreadyBanned);
|
||||
|
||||
assertThat(banUuidsChange.getDeleteBannedMembersCount(), is(2));
|
||||
assertThat(banUuidsChange.getDeleteBannedMembersList()
|
||||
.stream()
|
||||
.map(DeleteBannedMemberAction::getDeletedUserId)
|
||||
.collect(Collectors.toList()),
|
||||
hasItems(oldest.get(0), oldest.get(1)));
|
||||
|
||||
|
||||
assertThat(banUuidsChange.getAddBannedMembersCount(), is(2));
|
||||
assertThat(banUuidsChange.getAddBannedMembersList()
|
||||
.stream()
|
||||
.map(AddBannedMemberAction::getAdded)
|
||||
.map(BannedMember::getUserId)
|
||||
.collect(Collectors.toList()),
|
||||
hasItems(groupOperations.encryptUuid(toBan.get(0)), groupOperations.encryptUuid(toBan.get(1))));
|
||||
}
|
||||
}
|
||||
@@ -58,7 +58,7 @@ public final class GroupsV2Operations_decrypt_change_Test {
|
||||
server = new TestZkGroupServer();
|
||||
groupSecretParams = GroupSecretParams.deriveFromMasterKey(new GroupMasterKey(Util.getSecretBytes(32)));
|
||||
clientZkOperations = new ClientZkOperations(server.getServerPublicParams());
|
||||
groupOperations = new GroupsV2Operations(clientZkOperations).forGroup(groupSecretParams);
|
||||
groupOperations = new GroupsV2Operations(clientZkOperations, 1000).forGroup(groupSecretParams);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -157,7 +157,7 @@ public final class GroupsV2Operations_decrypt_change_Test {
|
||||
public void can_decrypt_member_removals_field4() {
|
||||
UUID oldMember = UUID.randomUUID();
|
||||
|
||||
assertDecryption(groupOperations.createRemoveMembersChange(Collections.singleton(oldMember), false)
|
||||
assertDecryption(groupOperations.createRemoveMembersChange(Collections.singleton(oldMember), false, Collections.emptyList())
|
||||
.setRevision(10),
|
||||
DecryptedGroupChange.newBuilder()
|
||||
.setRevision(10)
|
||||
@@ -341,7 +341,7 @@ public final class GroupsV2Operations_decrypt_change_Test {
|
||||
public void can_decrypt_member_requests_refusals_field17() {
|
||||
UUID newRequestingMember = UUID.randomUUID();
|
||||
|
||||
assertDecryption(groupOperations.createRefuseGroupJoinRequest(Collections.singleton(newRequestingMember), true)
|
||||
assertDecryption(groupOperations.createRefuseGroupJoinRequest(Collections.singleton(newRequestingMember), true, Collections.emptyList())
|
||||
.setRevision(10),
|
||||
DecryptedGroupChange.newBuilder()
|
||||
.setRevision(10)
|
||||
@@ -393,7 +393,7 @@ public final class GroupsV2Operations_decrypt_change_Test {
|
||||
public void can_decrypt_member_bans_field22() {
|
||||
UUID ban = UUID.randomUUID();
|
||||
|
||||
assertDecryption(groupOperations.createBanUuidsChange(Collections.singleton(ban), false)
|
||||
assertDecryption(groupOperations.createBanUuidsChange(Collections.singleton(ban), false, Collections.emptyList())
|
||||
.setRevision(13),
|
||||
DecryptedGroupChange.newBuilder()
|
||||
.setRevision(13)
|
||||
|
||||
@@ -28,7 +28,7 @@ public final class GroupsV2Operations_decrypt_groupJoinInfo_Test {
|
||||
ClientZkOperations clientZkOperations = new ClientZkOperations(server.getServerPublicParams());
|
||||
GroupSecretParams groupSecretParams = GroupSecretParams.deriveFromMasterKey(new GroupMasterKey(Util.getSecretBytes(32)));
|
||||
|
||||
groupOperations = new GroupsV2Operations(clientZkOperations).forGroup(groupSecretParams);
|
||||
groupOperations = new GroupsV2Operations(clientZkOperations, 1000).forGroup(groupSecretParams);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -44,7 +44,7 @@ public final class GroupsV2Operations_decrypt_group_Test {
|
||||
ClientZkOperations clientZkOperations = new ClientZkOperations(server.getServerPublicParams());
|
||||
|
||||
groupSecretParams = GroupSecretParams.deriveFromMasterKey(new GroupMasterKey(Util.getSecretBytes(32)));
|
||||
groupOperations = new GroupsV2Operations(clientZkOperations).forGroup(groupSecretParams);
|
||||
groupOperations = new GroupsV2Operations(clientZkOperations, 1000).forGroup(groupSecretParams);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user