kopia lustrzana https://github.com/ryukoposting/Signal-Android
Enforce limit for total number of blocked requests.
rodzic
b3d9a85fa2
commit
6890973ce8
|
@ -105,7 +105,7 @@ public class ApplicationDependencyProvider implements ApplicationDependencies.Pr
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @NonNull GroupsV2Operations provideGroupsV2Operations() {
|
public @NonNull GroupsV2Operations provideGroupsV2Operations() {
|
||||||
return new GroupsV2Operations(provideClientZkOperations());
|
return new GroupsV2Operations(provideClientZkOperations(), FeatureFlags.groupLimits().getHardLimit());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -283,19 +283,16 @@ public final class GroupManager {
|
||||||
@NonNull RecipientId recipientId)
|
@NonNull RecipientId recipientId)
|
||||||
throws GroupChangeBusyException, IOException, GroupChangeFailedException, GroupNotAMemberException, GroupInsufficientRightsException
|
throws GroupChangeBusyException, IOException, GroupChangeFailedException, GroupNotAMemberException, GroupInsufficientRightsException
|
||||||
{
|
{
|
||||||
GroupDatabase.GroupRecord groupRecord = SignalDatabase.groups().requireGroup(groupId);
|
GroupDatabase.V2GroupProperties groupProperties = SignalDatabase.groups().requireGroup(groupId).requireV2GroupProperties();
|
||||||
Recipient recipient = Recipient.resolved(recipientId);
|
Recipient recipient = Recipient.resolved(recipientId);
|
||||||
|
|
||||||
if (groupRecord.requireV2GroupProperties().getBannedMembers().contains(recipient.requireServiceId().uuid())) {
|
if (groupProperties.getBannedMembers().contains(recipient.requireServiceId().uuid())) {
|
||||||
Log.i(TAG, "Attempt to ban already banned recipient: " + recipientId);
|
Log.i(TAG, "Attempt to ban already banned recipient: " + recipientId);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ByteString uuid = UuidUtil.toByteString(recipient.requireServiceId().uuid());
|
|
||||||
boolean rejectJoinRequest = groupRecord.requireV2GroupProperties().getDecryptedGroup().getRequestingMembersList().stream().anyMatch(m -> m.getUuid().equals(uuid));
|
|
||||||
|
|
||||||
try (GroupManagerV2.GroupEditor editor = new GroupManagerV2(context).edit(groupId.requireV2())) {
|
try (GroupManagerV2.GroupEditor editor = new GroupManagerV2(context).edit(groupId.requireV2())) {
|
||||||
editor.ban(Collections.singleton(recipient.requireServiceId().uuid()), rejectJoinRequest);
|
editor.ban(recipient.requireServiceId().uuid());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -309,6 +309,7 @@ final class GroupManagerV2 {
|
||||||
final class GroupEditor extends LockOwner {
|
final class GroupEditor extends LockOwner {
|
||||||
|
|
||||||
private final GroupId.V2 groupId;
|
private final GroupId.V2 groupId;
|
||||||
|
private final GroupDatabase.V2GroupProperties v2GroupProperties;
|
||||||
private final GroupMasterKey groupMasterKey;
|
private final GroupMasterKey groupMasterKey;
|
||||||
private final GroupSecretParams groupSecretParams;
|
private final GroupSecretParams groupSecretParams;
|
||||||
private final GroupsV2Operations.GroupOperations groupOperations;
|
private final GroupsV2Operations.GroupOperations groupOperations;
|
||||||
|
@ -316,10 +317,10 @@ final class GroupManagerV2 {
|
||||||
GroupEditor(@NonNull GroupId.V2 groupId, @NonNull Closeable lock) {
|
GroupEditor(@NonNull GroupId.V2 groupId, @NonNull Closeable lock) {
|
||||||
super(lock);
|
super(lock);
|
||||||
|
|
||||||
GroupDatabase.GroupRecord groupRecord = groupDatabase.requireGroup(groupId);
|
GroupDatabase.GroupRecord groupRecord = groupDatabase.requireGroup(groupId);
|
||||||
GroupDatabase.V2GroupProperties v2GroupProperties = groupRecord.requireV2GroupProperties();
|
|
||||||
|
|
||||||
this.groupId = groupId;
|
this.groupId = groupId;
|
||||||
|
this.v2GroupProperties = groupRecord.requireV2GroupProperties();
|
||||||
this.groupMasterKey = v2GroupProperties.getGroupMasterKey();
|
this.groupMasterKey = v2GroupProperties.getGroupMasterKey();
|
||||||
this.groupSecretParams = GroupSecretParams.deriveFromMasterKey(groupMasterKey);
|
this.groupSecretParams = GroupSecretParams.deriveFromMasterKey(groupMasterKey);
|
||||||
this.groupOperations = groupsV2Operations.forGroup(groupSecretParams);
|
this.groupOperations = groupsV2Operations.forGroup(groupSecretParams);
|
||||||
|
@ -428,7 +429,7 @@ final class GroupManagerV2 {
|
||||||
.map(r -> Recipient.resolved(r).requireServiceId().uuid())
|
.map(r -> Recipient.resolved(r).requireServiceId().uuid())
|
||||||
.collect(Collectors.toSet());
|
.collect(Collectors.toSet());
|
||||||
|
|
||||||
return commitChangeWithConflictResolution(groupOperations.createRefuseGroupJoinRequest(uuids, true));
|
return commitChangeWithConflictResolution(groupOperations.createRefuseGroupJoinRequest(uuids, true, v2GroupProperties.getDecryptedGroup().getBannedMembersList()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@WorkerThread
|
@WorkerThread
|
||||||
|
@ -463,7 +464,11 @@ final class GroupManagerV2 {
|
||||||
@NonNull GroupManager.GroupActionResult ejectMember(@NonNull ServiceId serviceId, boolean allowWhenBlocked, boolean ban)
|
@NonNull GroupManager.GroupActionResult ejectMember(@NonNull ServiceId serviceId, boolean allowWhenBlocked, boolean ban)
|
||||||
throws GroupChangeFailedException, GroupInsufficientRightsException, IOException, GroupNotAMemberException
|
throws GroupChangeFailedException, GroupInsufficientRightsException, IOException, GroupNotAMemberException
|
||||||
{
|
{
|
||||||
return commitChangeWithConflictResolution(groupOperations.createRemoveMembersChange(Collections.singleton(serviceId.uuid()), ban), allowWhenBlocked);
|
return commitChangeWithConflictResolution(groupOperations.createRemoveMembersChange(Collections.singleton(serviceId.uuid()),
|
||||||
|
ban,
|
||||||
|
ban ? v2GroupProperties.getDecryptedGroup().getBannedMembersList()
|
||||||
|
: Collections.emptyList()),
|
||||||
|
allowWhenBlocked);
|
||||||
}
|
}
|
||||||
|
|
||||||
@WorkerThread
|
@WorkerThread
|
||||||
|
@ -530,11 +535,18 @@ final class GroupManagerV2 {
|
||||||
return commitChangeWithConflictResolution(groupOperations.createAcceptInviteChange(groupCandidate.getProfileKeyCredential().get()));
|
return commitChangeWithConflictResolution(groupOperations.createAcceptInviteChange(groupCandidate.getProfileKeyCredential().get()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public GroupManager.GroupActionResult ban(Set<UUID> uuids, boolean rejectJoinRequest) throws GroupChangeFailedException, GroupNotAMemberException, GroupInsufficientRightsException, IOException {
|
public GroupManager.GroupActionResult ban(UUID uuid)
|
||||||
return commitChangeWithConflictResolution(groupOperations.createBanUuidsChange(uuids, rejectJoinRequest));
|
throws GroupChangeFailedException, GroupNotAMemberException, GroupInsufficientRightsException, IOException
|
||||||
|
{
|
||||||
|
ByteString uuidByteString = UuidUtil.toByteString(uuid);
|
||||||
|
boolean rejectJoinRequest = v2GroupProperties.getDecryptedGroup().getRequestingMembersList().stream().anyMatch(m -> m.getUuid().equals(uuidByteString));
|
||||||
|
|
||||||
|
return commitChangeWithConflictResolution(groupOperations.createBanUuidsChange(Collections.singleton(uuid), rejectJoinRequest, v2GroupProperties.getDecryptedGroup().getBannedMembersList()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public GroupManager.GroupActionResult unban(Set<UUID> uuids) throws GroupChangeFailedException, GroupNotAMemberException, GroupInsufficientRightsException, IOException {
|
public GroupManager.GroupActionResult unban(Set<UUID> uuids)
|
||||||
|
throws GroupChangeFailedException, GroupNotAMemberException, GroupInsufficientRightsException, IOException
|
||||||
|
{
|
||||||
return commitChangeWithConflictResolution(groupOperations.createUnbanUuidsChange(uuids));
|
return commitChangeWithConflictResolution(groupOperations.createUnbanUuidsChange(uuids));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1102,7 +1114,7 @@ final class GroupManagerV2 {
|
||||||
|
|
||||||
GroupChange signedGroupChange;
|
GroupChange signedGroupChange;
|
||||||
try {
|
try {
|
||||||
signedGroupChange = commitCancelChangeWithConflictResolution(groupOperations.createRefuseGroupJoinRequest(uuids, false));
|
signedGroupChange = commitCancelChangeWithConflictResolution(groupOperations.createRefuseGroupJoinRequest(uuids, false, Collections.emptyList()));
|
||||||
} catch (GroupLinkNotActiveException e) {
|
} catch (GroupLinkNotActiveException e) {
|
||||||
Log.d(TAG, "Unexpected unable to leave group due to group link off");
|
Log.d(TAG, "Unexpected unable to leave group due to group link off");
|
||||||
throw new GroupChangeFailedException(e);
|
throw new GroupChangeFailedException(e);
|
||||||
|
|
|
@ -43,7 +43,8 @@ public class AccountManagerFactory {
|
||||||
deviceId,
|
deviceId,
|
||||||
password,
|
password,
|
||||||
BuildConfig.SIGNAL_AGENT,
|
BuildConfig.SIGNAL_AGENT,
|
||||||
FeatureFlags.okHttpAutomaticRetry());
|
FeatureFlags.okHttpAutomaticRetry(),
|
||||||
|
FeatureFlags.groupLimits().getHardLimit());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -65,7 +66,14 @@ public class AccountManagerFactory {
|
||||||
}
|
}
|
||||||
|
|
||||||
return new SignalServiceAccountManager(new SignalServiceNetworkAccess(context).getConfiguration(number),
|
return new SignalServiceAccountManager(new SignalServiceNetworkAccess(context).getConfiguration(number),
|
||||||
null, null, number, deviceId, password, BuildConfig.SIGNAL_AGENT, FeatureFlags.okHttpAutomaticRetry());
|
null,
|
||||||
|
null,
|
||||||
|
number,
|
||||||
|
deviceId,
|
||||||
|
password,
|
||||||
|
BuildConfig.SIGNAL_AGENT,
|
||||||
|
FeatureFlags.okHttpAutomaticRetry(),
|
||||||
|
FeatureFlags.groupLimits().getHardLimit());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -85,7 +85,7 @@ class GroupManagerV2Test_edit {
|
||||||
|
|
||||||
groupDatabase = mock(GroupDatabase::class.java)
|
groupDatabase = mock(GroupDatabase::class.java)
|
||||||
groupsV2API = mock(GroupsV2Api::class.java)
|
groupsV2API = mock(GroupsV2Api::class.java)
|
||||||
groupsV2Operations = GroupsV2Operations(clientZkOperations)
|
groupsV2Operations = GroupsV2Operations(clientZkOperations, 1000)
|
||||||
groupsV2Authorization = mock(GroupsV2Authorization::class.java)
|
groupsV2Authorization = mock(GroupsV2Authorization::class.java)
|
||||||
groupsV2StateProcessor = mock(GroupsV2StateProcessor::class.java)
|
groupsV2StateProcessor = mock(GroupsV2StateProcessor::class.java)
|
||||||
groupCandidateHelper = mock(GroupCandidateHelper::class.java)
|
groupCandidateHelper = mock(GroupCandidateHelper::class.java)
|
||||||
|
|
|
@ -146,12 +146,13 @@ public class SignalServiceAccountManager {
|
||||||
int deviceId,
|
int deviceId,
|
||||||
String password,
|
String password,
|
||||||
String signalAgent,
|
String signalAgent,
|
||||||
boolean automaticNetworkRetry)
|
boolean automaticNetworkRetry,
|
||||||
|
int maxGroupSize)
|
||||||
{
|
{
|
||||||
this(configuration,
|
this(configuration,
|
||||||
new StaticCredentialsProvider(aci, pni, e164, deviceId, password),
|
new StaticCredentialsProvider(aci, pni, e164, deviceId, password),
|
||||||
signalAgent,
|
signalAgent,
|
||||||
new GroupsV2Operations(ClientZkOperations.create(configuration)),
|
new GroupsV2Operations(ClientZkOperations.create(configuration), maxGroupSize),
|
||||||
automaticNetworkRetry);
|
automaticNetworkRetry);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -108,9 +108,10 @@ public final class GroupChangeReconstruct {
|
||||||
builder.addNewPendingMembers(invitedMember);
|
builder.addNewPendingMembers(invitedMember);
|
||||||
}
|
}
|
||||||
|
|
||||||
Set<ByteString> consistentMemberUuids = intersect(fromStateMemberUuids, toStateMemberUuids);
|
Set<ByteString> consistentMemberUuids = intersect(fromStateMemberUuids, toStateMemberUuids);
|
||||||
Set<DecryptedMember> changedMembers = intersectByUUID(subtract(toState.getMembersList(), fromState.getMembersList()), consistentMemberUuids);
|
Set<DecryptedMember> changedMembers = intersectByUUID(subtract(toState.getMembersList(), fromState.getMembersList()), consistentMemberUuids);
|
||||||
Map<ByteString, DecryptedMember> membersUuidMap = uuidMap(fromState.getMembersList());
|
Map<ByteString, DecryptedMember> membersUuidMap = uuidMap(fromState.getMembersList());
|
||||||
|
Map<ByteString, DecryptedBannedMember> bannedMembersUuidMap = bannedUuidMap(toState.getBannedMembersList());
|
||||||
|
|
||||||
for (DecryptedMember newState : changedMembers) {
|
for (DecryptedMember newState : changedMembers) {
|
||||||
DecryptedMember oldState = membersUuidMap.get(newState.getUuid());
|
DecryptedMember oldState = membersUuidMap.get(newState.getUuid());
|
||||||
|
@ -152,7 +153,13 @@ public final class GroupChangeReconstruct {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (ByteString uuid : newBannedMemberUuids) {
|
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();
|
return builder.build();
|
||||||
|
@ -166,6 +173,14 @@ public final class GroupChangeReconstruct {
|
||||||
return map;
|
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) {
|
private static Set<DecryptedMember> intersectByUUID(Collection<DecryptedMember> members, Set<ByteString> uuids) {
|
||||||
Set<DecryptedMember> result = new LinkedHashSet<>(members.size());
|
Set<DecryptedMember> result = new LinkedHashSet<>(members.size());
|
||||||
for (DecryptedMember member : members) {
|
for (DecryptedMember member : members) {
|
||||||
|
|
|
@ -44,6 +44,7 @@ import org.whispersystems.signalservice.api.util.UuidUtil;
|
||||||
import java.security.SecureRandom;
|
import java.security.SecureRandom;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
@ -67,12 +68,14 @@ public final class GroupsV2Operations {
|
||||||
private final ServerPublicParams serverPublicParams;
|
private final ServerPublicParams serverPublicParams;
|
||||||
private final ClientZkProfileOperations clientZkProfileOperations;
|
private final ClientZkProfileOperations clientZkProfileOperations;
|
||||||
private final ClientZkAuthOperations clientZkAuthOperations;
|
private final ClientZkAuthOperations clientZkAuthOperations;
|
||||||
|
private final int maxGroupSize;
|
||||||
private final SecureRandom random;
|
private final SecureRandom random;
|
||||||
|
|
||||||
public GroupsV2Operations(ClientZkOperations clientZkOperations) {
|
public GroupsV2Operations(ClientZkOperations clientZkOperations, int maxGroupSize) {
|
||||||
this.serverPublicParams = clientZkOperations.getServerPublicParams();
|
this.serverPublicParams = clientZkOperations.getServerPublicParams();
|
||||||
this.clientZkProfileOperations = clientZkOperations.getProfileOperations();
|
this.clientZkProfileOperations = clientZkOperations.getProfileOperations();
|
||||||
this.clientZkAuthOperations = clientZkOperations.getAuthOperations();
|
this.clientZkAuthOperations = clientZkOperations.getAuthOperations();
|
||||||
|
this.maxGroupSize = maxGroupSize;
|
||||||
this.random = new SecureRandom();
|
this.random = new SecureRandom();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -209,8 +212,8 @@ public final class GroupsV2Operations {
|
||||||
return actions;
|
return actions;
|
||||||
}
|
}
|
||||||
|
|
||||||
public GroupChange.Actions.Builder createRefuseGroupJoinRequest(Set<UUID> requestsToRemove, boolean alsoBan) {
|
public GroupChange.Actions.Builder createRefuseGroupJoinRequest(Set<UUID> requestsToRemove, boolean alsoBan, List<DecryptedBannedMember> bannedMembers) {
|
||||||
GroupChange.Actions.Builder actions = alsoBan ? createBanUuidsChange(requestsToRemove, false)
|
GroupChange.Actions.Builder actions = alsoBan ? createBanUuidsChange(requestsToRemove, false, bannedMembers)
|
||||||
: GroupChange.Actions.newBuilder();
|
: GroupChange.Actions.newBuilder();
|
||||||
|
|
||||||
for (UUID uuid : requestsToRemove) {
|
for (UUID uuid : requestsToRemove) {
|
||||||
|
@ -235,8 +238,8 @@ public final class GroupsV2Operations {
|
||||||
return actions;
|
return actions;
|
||||||
}
|
}
|
||||||
|
|
||||||
public GroupChange.Actions.Builder createRemoveMembersChange(final Set<UUID> membersToRemove, boolean alsoBan) {
|
public GroupChange.Actions.Builder createRemoveMembersChange(final Set<UUID> membersToRemove, boolean alsoBan, List<DecryptedBannedMember> bannedMembers) {
|
||||||
GroupChange.Actions.Builder actions = alsoBan ? createBanUuidsChange(membersToRemove, false)
|
GroupChange.Actions.Builder actions = alsoBan ? createBanUuidsChange(membersToRemove, false, bannedMembers)
|
||||||
: GroupChange.Actions.newBuilder();
|
: GroupChange.Actions.newBuilder();
|
||||||
|
|
||||||
for (UUID remove: membersToRemove) {
|
for (UUID remove: membersToRemove) {
|
||||||
|
@ -249,7 +252,7 @@ public final class GroupsV2Operations {
|
||||||
}
|
}
|
||||||
|
|
||||||
public GroupChange.Actions.Builder createLeaveAndPromoteMembersToAdmin(UUID self, List<UUID> membersToMakeAdmin) {
|
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) {
|
for (UUID member : membersToMakeAdmin) {
|
||||||
actions.addModifyMemberRoles(GroupChange.Actions.ModifyMemberRoleAction
|
actions.addModifyMemberRoles(GroupChange.Actions.ModifyMemberRoleAction
|
||||||
|
@ -350,10 +353,23 @@ public final class GroupsV2Operations {
|
||||||
.setAnnouncementsOnly(isAnnouncementGroup));
|
.setAnnouncementsOnly(isAnnouncementGroup));
|
||||||
}
|
}
|
||||||
|
|
||||||
public GroupChange.Actions.Builder createBanUuidsChange(Set<UUID> banUuids, boolean rejectJoinRequest) {
|
public GroupChange.Actions.Builder createBanUuidsChange(Set<UUID> banUuids, boolean rejectJoinRequest, List<DecryptedBannedMember> bannedMembersList) {
|
||||||
GroupChange.Actions.Builder builder = rejectJoinRequest ? createRefuseGroupJoinRequest(banUuids, false)
|
GroupChange.Actions.Builder builder = rejectJoinRequest ? createRefuseGroupJoinRequest(banUuids, false, Collections.emptyList())
|
||||||
: GroupChange.Actions.newBuilder();
|
: 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) {
|
for (UUID uuid : banUuids) {
|
||||||
builder.addAddBannedMembers(GroupChange.Actions.AddBannedMemberAction.newBuilder().setAdded(BannedMember.newBuilder().setUserId(encryptUuid(uuid)).build()));
|
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()) {
|
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()
|
return DecryptedGroup.newBuilder()
|
||||||
|
@ -662,7 +678,7 @@ public final class GroupsV2Operations {
|
||||||
|
|
||||||
// Field 22
|
// Field 22
|
||||||
for (GroupChange.Actions.AddBannedMemberAction action : actions.getAddBannedMembersList()) {
|
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
|
// Field 23
|
||||||
|
|
|
@ -34,7 +34,8 @@ message DecryptedRequestingMember {
|
||||||
}
|
}
|
||||||
|
|
||||||
message DecryptedBannedMember {
|
message DecryptedBannedMember {
|
||||||
bytes uuid = 1;
|
bytes uuid = 1;
|
||||||
|
uint64 timestamp = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
message DecryptedPendingMemberRemoval {
|
message DecryptedPendingMemberRemoval {
|
||||||
|
|
|
@ -46,7 +46,8 @@ message RequestingMember {
|
||||||
}
|
}
|
||||||
|
|
||||||
message BannedMember {
|
message BannedMember {
|
||||||
bytes userId = 1;
|
bytes userId = 1;
|
||||||
|
uint64 timestamp = 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
message AccessControl {
|
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();
|
server = new TestZkGroupServer();
|
||||||
groupSecretParams = GroupSecretParams.deriveFromMasterKey(new GroupMasterKey(Util.getSecretBytes(32)));
|
groupSecretParams = GroupSecretParams.deriveFromMasterKey(new GroupMasterKey(Util.getSecretBytes(32)));
|
||||||
clientZkOperations = new ClientZkOperations(server.getServerPublicParams());
|
clientZkOperations = new ClientZkOperations(server.getServerPublicParams());
|
||||||
groupOperations = new GroupsV2Operations(clientZkOperations).forGroup(groupSecretParams);
|
groupOperations = new GroupsV2Operations(clientZkOperations, 1000).forGroup(groupSecretParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -157,7 +157,7 @@ public final class GroupsV2Operations_decrypt_change_Test {
|
||||||
public void can_decrypt_member_removals_field4() {
|
public void can_decrypt_member_removals_field4() {
|
||||||
UUID oldMember = UUID.randomUUID();
|
UUID oldMember = UUID.randomUUID();
|
||||||
|
|
||||||
assertDecryption(groupOperations.createRemoveMembersChange(Collections.singleton(oldMember), false)
|
assertDecryption(groupOperations.createRemoveMembersChange(Collections.singleton(oldMember), false, Collections.emptyList())
|
||||||
.setRevision(10),
|
.setRevision(10),
|
||||||
DecryptedGroupChange.newBuilder()
|
DecryptedGroupChange.newBuilder()
|
||||||
.setRevision(10)
|
.setRevision(10)
|
||||||
|
@ -341,7 +341,7 @@ public final class GroupsV2Operations_decrypt_change_Test {
|
||||||
public void can_decrypt_member_requests_refusals_field17() {
|
public void can_decrypt_member_requests_refusals_field17() {
|
||||||
UUID newRequestingMember = UUID.randomUUID();
|
UUID newRequestingMember = UUID.randomUUID();
|
||||||
|
|
||||||
assertDecryption(groupOperations.createRefuseGroupJoinRequest(Collections.singleton(newRequestingMember), true)
|
assertDecryption(groupOperations.createRefuseGroupJoinRequest(Collections.singleton(newRequestingMember), true, Collections.emptyList())
|
||||||
.setRevision(10),
|
.setRevision(10),
|
||||||
DecryptedGroupChange.newBuilder()
|
DecryptedGroupChange.newBuilder()
|
||||||
.setRevision(10)
|
.setRevision(10)
|
||||||
|
@ -393,7 +393,7 @@ public final class GroupsV2Operations_decrypt_change_Test {
|
||||||
public void can_decrypt_member_bans_field22() {
|
public void can_decrypt_member_bans_field22() {
|
||||||
UUID ban = UUID.randomUUID();
|
UUID ban = UUID.randomUUID();
|
||||||
|
|
||||||
assertDecryption(groupOperations.createBanUuidsChange(Collections.singleton(ban), false)
|
assertDecryption(groupOperations.createBanUuidsChange(Collections.singleton(ban), false, Collections.emptyList())
|
||||||
.setRevision(13),
|
.setRevision(13),
|
||||||
DecryptedGroupChange.newBuilder()
|
DecryptedGroupChange.newBuilder()
|
||||||
.setRevision(13)
|
.setRevision(13)
|
||||||
|
|
|
@ -28,7 +28,7 @@ public final class GroupsV2Operations_decrypt_groupJoinInfo_Test {
|
||||||
ClientZkOperations clientZkOperations = new ClientZkOperations(server.getServerPublicParams());
|
ClientZkOperations clientZkOperations = new ClientZkOperations(server.getServerPublicParams());
|
||||||
GroupSecretParams groupSecretParams = GroupSecretParams.deriveFromMasterKey(new GroupMasterKey(Util.getSecretBytes(32)));
|
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());
|
ClientZkOperations clientZkOperations = new ClientZkOperations(server.getServerPublicParams());
|
||||||
|
|
||||||
groupSecretParams = GroupSecretParams.deriveFromMasterKey(new GroupMasterKey(Util.getSecretBytes(32)));
|
groupSecretParams = GroupSecretParams.deriveFromMasterKey(new GroupMasterKey(Util.getSecretBytes(32)));
|
||||||
groupOperations = new GroupsV2Operations(clientZkOperations).forGroup(groupSecretParams);
|
groupOperations = new GroupsV2Operations(clientZkOperations, 1000).forGroup(groupSecretParams);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Ładowanie…
Reference in New Issue