kopia lustrzana https://github.com/ryukoposting/Signal-Android
Prevent rejected/kicked group members from joining again via group link.
rodzic
3503c60fd1
commit
eed45b57a1
|
@ -12,9 +12,11 @@ import org.signal.storageservice.protos.groups.local.DecryptedGroup;
|
|||
import org.signal.storageservice.protos.groups.local.DecryptedGroupJoinInfo;
|
||||
import org.signal.zkgroup.VerificationFailedException;
|
||||
import org.signal.zkgroup.groups.GroupMasterKey;
|
||||
import org.signal.zkgroup.groups.GroupSecretParams;
|
||||
import org.signal.zkgroup.groups.UuidCiphertext;
|
||||
import org.thoughtcrime.securesms.database.GroupDatabase;
|
||||
import org.thoughtcrime.securesms.database.SignalDatabase;
|
||||
import org.thoughtcrime.securesms.dependencies.ApplicationDependencies;
|
||||
import org.thoughtcrime.securesms.groups.v2.GroupInviteLinkUrl;
|
||||
import org.thoughtcrime.securesms.groups.v2.GroupLinkPassword;
|
||||
import org.thoughtcrime.securesms.keyvalue.SignalStore;
|
||||
|
@ -22,9 +24,11 @@ import org.thoughtcrime.securesms.profiles.AvatarHelper;
|
|||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
import org.whispersystems.signalservice.api.groupsv2.GroupLinkNotActiveException;
|
||||
import org.whispersystems.signalservice.api.groupsv2.GroupsV2Operations;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
@ -157,11 +161,11 @@ public final class GroupManager {
|
|||
}
|
||||
|
||||
@WorkerThread
|
||||
public static void ejectFromGroup(@NonNull Context context, @NonNull GroupId.V2 groupId, @NonNull Recipient recipient)
|
||||
public static void ejectAndBanFromGroup(@NonNull Context context, @NonNull GroupId.V2 groupId, @NonNull Recipient recipient)
|
||||
throws GroupChangeBusyException, GroupChangeFailedException, GroupInsufficientRightsException, GroupNotAMemberException, IOException
|
||||
{
|
||||
try (GroupManagerV2.GroupEditor edit = new GroupManagerV2(context).edit(groupId.requireV2())) {
|
||||
edit.ejectMember(recipient.requireServiceId(), false);
|
||||
edit.ejectMember(recipient.requireServiceId(), false, true);
|
||||
Log.i(TAG, "Member removed from group " + groupId);
|
||||
}
|
||||
}
|
||||
|
@ -273,6 +277,28 @@ public final class GroupManager {
|
|||
}
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
public static void ban(@NonNull Context context,
|
||||
@NonNull GroupId.V2 groupId,
|
||||
@NonNull RecipientId recipientId)
|
||||
throws GroupChangeBusyException, IOException, GroupChangeFailedException, GroupNotAMemberException, GroupInsufficientRightsException
|
||||
{
|
||||
try (GroupManagerV2.GroupEditor editor = new GroupManagerV2(context).edit(groupId.requireV2())) {
|
||||
editor.ban(Collections.singleton(Recipient.resolved(recipientId).requireServiceId().uuid()));
|
||||
}
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
public static void unban(@NonNull Context context,
|
||||
@NonNull GroupId.V2 groupId,
|
||||
@NonNull RecipientId recipientId)
|
||||
throws GroupChangeBusyException, IOException, GroupChangeFailedException, GroupNotAMemberException, GroupInsufficientRightsException
|
||||
{
|
||||
try (GroupManagerV2.GroupEditor editor = new GroupManagerV2(context).edit(groupId.requireV2())) {
|
||||
editor.unban(Collections.singleton(Recipient.resolved(recipientId).requireServiceId().uuid()));
|
||||
}
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
public static void applyMembershipAdditionRightsChange(@NonNull Context context,
|
||||
@NonNull GroupId.V2 groupId,
|
||||
|
|
|
@ -428,7 +428,7 @@ final class GroupManagerV2 {
|
|||
.map(r -> Recipient.resolved(r).requireServiceId().uuid())
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
return commitChangeWithConflictResolution(groupOperations.createRefuseGroupJoinRequest(uuids));
|
||||
return commitChangeWithConflictResolution(groupOperations.createRefuseGroupJoinRequest(uuids, true));
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
|
@ -455,15 +455,15 @@ final class GroupManagerV2 {
|
|||
throw new AssertionError(e);
|
||||
}
|
||||
} else {
|
||||
return ejectMember(ServiceId.from(selfAci.uuid()), true);
|
||||
return ejectMember(ServiceId.from(selfAci.uuid()), true, false);
|
||||
}
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
@NonNull GroupManager.GroupActionResult ejectMember(@NonNull ServiceId serviceId, boolean allowWhenBlocked)
|
||||
@NonNull GroupManager.GroupActionResult ejectMember(@NonNull ServiceId serviceId, boolean allowWhenBlocked, boolean ban)
|
||||
throws GroupChangeFailedException, GroupInsufficientRightsException, IOException, GroupNotAMemberException
|
||||
{
|
||||
return commitChangeWithConflictResolution(groupOperations.createRemoveMembersChange(Collections.singleton(serviceId.uuid())), allowWhenBlocked);
|
||||
return commitChangeWithConflictResolution(groupOperations.createRemoveMembersChange(Collections.singleton(serviceId.uuid()), ban), allowWhenBlocked);
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
|
@ -530,6 +530,14 @@ final class GroupManagerV2 {
|
|||
return commitChangeWithConflictResolution(groupOperations.createAcceptInviteChange(groupCandidate.getProfileKeyCredential().get()));
|
||||
}
|
||||
|
||||
public GroupManager.GroupActionResult ban(Set<UUID> uuids) throws GroupChangeFailedException, GroupNotAMemberException, GroupInsufficientRightsException, IOException {
|
||||
return commitChangeWithConflictResolution(groupOperations.createBanUuidsChange(uuids));
|
||||
}
|
||||
|
||||
public GroupManager.GroupActionResult unban(Set<UUID> uuids) throws GroupChangeFailedException, GroupNotAMemberException, GroupInsufficientRightsException, IOException {
|
||||
return commitChangeWithConflictResolution(groupOperations.createUnbanUuidsChange(uuids));
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
public GroupManager.GroupActionResult cycleGroupLinkPassword()
|
||||
throws GroupChangeFailedException, GroupNotAMemberException, GroupInsufficientRightsException, IOException
|
||||
|
@ -1094,7 +1102,7 @@ final class GroupManagerV2 {
|
|||
|
||||
GroupChange signedGroupChange;
|
||||
try {
|
||||
signedGroupChange = commitCancelChangeWithConflictResolution(groupOperations.createRefuseGroupJoinRequest(uuids));
|
||||
signedGroupChange = commitCancelChangeWithConflictResolution(groupOperations.createRefuseGroupJoinRequest(uuids, false));
|
||||
} catch (GroupLinkNotActiveException e) {
|
||||
Log.d(TAG, "Unexpected unable to leave group due to group link off");
|
||||
throw new GroupChangeFailedException(e);
|
||||
|
|
|
@ -5,6 +5,8 @@ import android.content.Context;
|
|||
import androidx.annotation.NonNull;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
|
||||
|
@ -13,50 +15,37 @@ final class RequestConfirmationDialog {
|
|||
private RequestConfirmationDialog() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirms that you want to approve or deny a request to join the group depending on
|
||||
* {@param approve}.
|
||||
*/
|
||||
static AlertDialog show(@NonNull Context context,
|
||||
@NonNull Recipient requester,
|
||||
boolean approve,
|
||||
@NonNull Runnable onApproveOrDeny)
|
||||
{
|
||||
if (approve) {
|
||||
return showRequestApproveConfirmationDialog(context, requester, onApproveOrDeny);
|
||||
} else {
|
||||
return showRequestDenyConfirmationDialog(context, requester, onApproveOrDeny);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirms that you want to approve a request to join the group.
|
||||
*/
|
||||
private static AlertDialog showRequestApproveConfirmationDialog(@NonNull Context context,
|
||||
@NonNull Recipient requester,
|
||||
@NonNull Runnable onApprove)
|
||||
public static AlertDialog showApprove(@NonNull Context context,
|
||||
@NonNull Recipient requester,
|
||||
@NonNull Runnable onApprove)
|
||||
{
|
||||
return new AlertDialog.Builder(context)
|
||||
.setMessage(context.getString(R.string.RequestConfirmationDialog_add_s_to_the_group,
|
||||
requester.getDisplayName(context)))
|
||||
.setPositiveButton(R.string.RequestConfirmationDialog_add, (dialog, which) -> onApprove.run())
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.show();
|
||||
return new MaterialAlertDialogBuilder(context)
|
||||
.setMessage(context.getString(R.string.RequestConfirmationDialog_add_s_to_the_group,
|
||||
requester.getDisplayName(context)))
|
||||
.setPositiveButton(R.string.RequestConfirmationDialog_add, (dialog, which) -> onApprove.run())
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.show();
|
||||
}
|
||||
|
||||
/**
|
||||
* Confirms that you want to deny a request to join the group.
|
||||
*/
|
||||
private static AlertDialog showRequestDenyConfirmationDialog(@NonNull Context context,
|
||||
@NonNull Recipient requester,
|
||||
@NonNull Runnable onDeny)
|
||||
public static AlertDialog showDeny(@NonNull Context context,
|
||||
@NonNull Recipient requester,
|
||||
boolean linkEnabled,
|
||||
@NonNull Runnable onDeny)
|
||||
{
|
||||
return new AlertDialog.Builder(context)
|
||||
.setMessage(context.getString(R.string.RequestConfirmationDialog_deny_request_from_s,
|
||||
requester.getDisplayName(context)))
|
||||
.setPositiveButton(R.string.RequestConfirmationDialog_deny, (dialog, which) -> onDeny.run())
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.show();
|
||||
String message = linkEnabled ? context.getString(R.string.RequestConfirmationDialog_deny_request_from_s_they_will_not_be_able_to_request, requester.getDisplayName(context))
|
||||
: context.getString(R.string.RequestConfirmationDialog_deny_request_from_s, requester.getDisplayName(context));
|
||||
|
||||
return new MaterialAlertDialogBuilder(context)
|
||||
.setMessage(message)
|
||||
.setPositiveButton(R.string.RequestConfirmationDialog_deny, (dialog, which) -> onDeny.run())
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.show();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -15,13 +15,11 @@ import org.thoughtcrime.securesms.groups.LiveGroup;
|
|||
import org.thoughtcrime.securesms.groups.ui.GroupChangeFailureReason;
|
||||
import org.thoughtcrime.securesms.groups.ui.GroupErrors;
|
||||
import org.thoughtcrime.securesms.groups.ui.GroupMemberEntry;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
import org.thoughtcrime.securesms.groups.v2.GroupLinkUrlAndStatus;
|
||||
import org.thoughtcrime.securesms.util.AsynchronousCallback;
|
||||
import org.thoughtcrime.securesms.util.SingleLiveEvent;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class RequestingMemberInvitesViewModel extends ViewModel {
|
||||
|
||||
|
@ -29,6 +27,7 @@ public class RequestingMemberInvitesViewModel extends ViewModel {
|
|||
private final RequestingMemberRepository requestingMemberRepository;
|
||||
private final MutableLiveData<String> toasts;
|
||||
private final LiveData<List<GroupMemberEntry.RequestingMember>> requesting;
|
||||
private final LiveData<GroupLinkUrlAndStatus> inviteLink;
|
||||
|
||||
private RequestingMemberInvitesViewModel(@NonNull Context context,
|
||||
@NonNull GroupId.V2 groupId,
|
||||
|
@ -36,62 +35,58 @@ public class RequestingMemberInvitesViewModel extends ViewModel {
|
|||
{
|
||||
this.context = context;
|
||||
this.requestingMemberRepository = requestingMemberRepository;
|
||||
this.requesting = new LiveGroup(groupId).getRequestingMembers();
|
||||
this.toasts = new SingleLiveEvent<>();
|
||||
|
||||
LiveGroup liveGroup = new LiveGroup(groupId);
|
||||
|
||||
this.requesting = liveGroup.getRequestingMembers();
|
||||
this.inviteLink = liveGroup.getGroupLink();
|
||||
}
|
||||
|
||||
LiveData<List<GroupMemberEntry.RequestingMember>> getRequesting() {
|
||||
return requesting;
|
||||
}
|
||||
|
||||
LiveData<GroupLinkUrlAndStatus> getInviteLink() {
|
||||
return inviteLink;
|
||||
}
|
||||
|
||||
LiveData<String> getToasts() {
|
||||
return toasts;
|
||||
}
|
||||
|
||||
void approveRequestFor(@NonNull GroupMemberEntry.RequestingMember requestingMember) {
|
||||
approveOrDeny(requestingMember, true);
|
||||
requestingMember.setBusy(true);
|
||||
requestingMemberRepository.approveRequest(requestingMember.getRequester(), new AsynchronousCallback.WorkerThread<Void, GroupChangeFailureReason>() {
|
||||
@Override
|
||||
public void onComplete(@Nullable Void result) {
|
||||
requestingMember.setBusy(false);
|
||||
toasts.postValue(context.getString(R.string.RequestingMembersFragment_added_s, requestingMember.getRequester().getDisplayName(context)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(@Nullable GroupChangeFailureReason error) {
|
||||
requestingMember.setBusy(false);
|
||||
toasts.postValue(context.getString(GroupErrors.getUserDisplayMessage(error)));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void denyRequestFor(@NonNull GroupMemberEntry.RequestingMember requestingMember) {
|
||||
approveOrDeny(requestingMember, false);
|
||||
}
|
||||
requestingMember.setBusy(true);
|
||||
requestingMemberRepository.denyRequest(requestingMember.getRequester(), new AsynchronousCallback.WorkerThread<Void, GroupChangeFailureReason>() {
|
||||
@Override
|
||||
public void onComplete(@Nullable Void result) {
|
||||
requestingMember.setBusy(false);
|
||||
toasts.postValue(context.getString(R.string.RequestingMembersFragment_denied_s, requestingMember.getRequester().getDisplayName(context)));
|
||||
}
|
||||
|
||||
private void approveOrDeny(@NonNull GroupMemberEntry.RequestingMember requestingMember, boolean approve) {
|
||||
RequestConfirmationDialog.show(context, requestingMember.getRequester(), approve, () -> {
|
||||
Set<RecipientId> memberAsSet = Collections.singleton(requestingMember.getRequester().getId());
|
||||
|
||||
if (approve) {
|
||||
requestingMember.setBusy(true);
|
||||
requestingMemberRepository.approveRequests(memberAsSet, new AsynchronousCallback.WorkerThread<Void, GroupChangeFailureReason>() {
|
||||
@Override
|
||||
public void onComplete(@Nullable Void result) {
|
||||
requestingMember.setBusy(false);
|
||||
toasts.postValue(context.getString(R.string.RequestingMembersFragment_added_s, requestingMember.getRequester().getDisplayName(context)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(@Nullable GroupChangeFailureReason error) {
|
||||
requestingMember.setBusy(false);
|
||||
toasts.postValue(context.getString(GroupErrors.getUserDisplayMessage(error)));
|
||||
}
|
||||
});
|
||||
} else {
|
||||
requestingMember.setBusy(true);
|
||||
requestingMemberRepository.denyRequests(memberAsSet, new AsynchronousCallback.WorkerThread<Void, GroupChangeFailureReason>() {
|
||||
@Override
|
||||
public void onComplete(@Nullable Void result) {
|
||||
requestingMember.setBusy(false);
|
||||
toasts.postValue(context.getString(R.string.RequestingMembersFragment_denied_s, requestingMember.getRequester().getDisplayName(context)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(@Nullable GroupChangeFailureReason error) {
|
||||
requestingMember.setBusy(false);
|
||||
toasts.postValue(context.getString(GroupErrors.getUserDisplayMessage(error)));
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
@Override
|
||||
public void onError(@Nullable GroupChangeFailureReason error) {
|
||||
requestingMember.setBusy(false);
|
||||
toasts.postValue(context.getString(GroupErrors.getUserDisplayMessage(error)));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static class Factory implements ViewModelProvider.Factory {
|
||||
|
|
|
@ -10,11 +10,13 @@ import org.thoughtcrime.securesms.groups.GroupChangeException;
|
|||
import org.thoughtcrime.securesms.groups.GroupId;
|
||||
import org.thoughtcrime.securesms.groups.GroupManager;
|
||||
import org.thoughtcrime.securesms.groups.ui.GroupChangeFailureReason;
|
||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||
import org.thoughtcrime.securesms.recipients.RecipientId;
|
||||
import org.thoughtcrime.securesms.util.AsynchronousCallback;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
|
||||
/**
|
||||
* Repository for modifying the requesting members on a single group.
|
||||
|
@ -31,12 +33,12 @@ final class RequestingMemberRepository {
|
|||
this.groupId = groupId;
|
||||
}
|
||||
|
||||
void approveRequests(@NonNull Collection<RecipientId> recipientIds,
|
||||
@NonNull AsynchronousCallback.WorkerThread<Void, GroupChangeFailureReason> callback)
|
||||
void approveRequest(@NonNull Recipient recipient,
|
||||
@NonNull AsynchronousCallback.WorkerThread<Void, GroupChangeFailureReason> callback)
|
||||
{
|
||||
SignalExecutors.UNBOUNDED.execute(() -> {
|
||||
try {
|
||||
GroupManager.approveRequests(context, groupId, recipientIds);
|
||||
GroupManager.approveRequests(context, groupId, Collections.singleton(recipient.getId()));
|
||||
callback.onComplete(null);
|
||||
} catch (GroupChangeException | IOException e) {
|
||||
Log.w(TAG, e);
|
||||
|
@ -45,12 +47,12 @@ final class RequestingMemberRepository {
|
|||
});
|
||||
}
|
||||
|
||||
void denyRequests(@NonNull Collection<RecipientId> recipientIds,
|
||||
@NonNull AsynchronousCallback.WorkerThread<Void, GroupChangeFailureReason> callback)
|
||||
void denyRequest(@NonNull Recipient recipient,
|
||||
@NonNull AsynchronousCallback.WorkerThread<Void, GroupChangeFailureReason> callback)
|
||||
{
|
||||
SignalExecutors.UNBOUNDED.execute(() -> {
|
||||
try {
|
||||
GroupManager.denyRequests(context, groupId, recipientIds);
|
||||
GroupManager.denyRequests(context, groupId, Collections.singleton(recipient.getId()));
|
||||
callback.onComplete(null);
|
||||
} catch (GroupChangeException | IOException e) {
|
||||
Log.w(TAG, e);
|
||||
|
|
|
@ -9,6 +9,7 @@ import android.widget.Toast;
|
|||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
import androidx.lifecycle.ViewModelProviders;
|
||||
|
||||
import org.thoughtcrime.securesms.R;
|
||||
|
@ -16,6 +17,7 @@ import org.thoughtcrime.securesms.groups.GroupId;
|
|||
import org.thoughtcrime.securesms.groups.ui.AdminActionsListener;
|
||||
import org.thoughtcrime.securesms.groups.ui.GroupMemberEntry;
|
||||
import org.thoughtcrime.securesms.groups.ui.GroupMemberListView;
|
||||
import org.thoughtcrime.securesms.groups.v2.GroupLinkUrlAndStatus;
|
||||
import org.thoughtcrime.securesms.recipients.ui.bottomsheet.RecipientBottomSheetDialogFragment;
|
||||
import org.thoughtcrime.securesms.util.BottomSheetUtil;
|
||||
|
||||
|
@ -29,9 +31,9 @@ public class RequestingMembersFragment extends Fragment {
|
|||
private static final String GROUP_ID = "GROUP_ID";
|
||||
|
||||
private RequestingMemberInvitesViewModel viewModel;
|
||||
private GroupMemberListView requestingMembers;
|
||||
private View noRequestingMessage;
|
||||
private View requestingExplanation;
|
||||
private GroupMemberListView requestingMembers;
|
||||
private View noRequestingMessage;
|
||||
private View requestingExplanation;
|
||||
|
||||
public static RequestingMembersFragment newInstance(@NonNull GroupId.V2 groupId) {
|
||||
RequestingMembersFragment fragment = new RequestingMembersFragment();
|
||||
|
@ -53,9 +55,10 @@ public class RequestingMembersFragment extends Fragment {
|
|||
|
||||
requestingMembers.initializeAdapter(getViewLifecycleOwner());
|
||||
|
||||
requestingMembers.setRecipientClickListener(recipient ->
|
||||
requestingMembers.setRecipientClickListener(recipient -> {
|
||||
RecipientBottomSheetDialogFragment.create(recipient.getId(), null)
|
||||
.show(requireActivity().getSupportFragmentManager(), BottomSheetUtil.STANDARD_BOTTOM_SHEET_FRAGMENT_TAG));
|
||||
.show(requireActivity().getSupportFragmentManager(), BottomSheetUtil.STANDARD_BOTTOM_SHEET_FRAGMENT_TAG);
|
||||
});
|
||||
|
||||
requestingMembers.setAdminActionsListener(new AdminActionsListener() {
|
||||
|
||||
|
@ -71,31 +74,37 @@ public class RequestingMembersFragment extends Fragment {
|
|||
|
||||
@Override
|
||||
public void onApproveRequest(@NonNull GroupMemberEntry.RequestingMember requestingMember) {
|
||||
viewModel.approveRequestFor(requestingMember);
|
||||
//noinspection CodeBlock2Expr
|
||||
RequestConfirmationDialog.showApprove(requireContext(), requestingMember.getRequester(), () -> {
|
||||
viewModel.approveRequestFor(requestingMember);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDenyRequest(@NonNull GroupMemberEntry.RequestingMember requestingMember) {
|
||||
viewModel.denyRequestFor(requestingMember);
|
||||
GroupLinkUrlAndStatus linkStatus = viewModel.getInviteLink().getValue();
|
||||
boolean linkEnabled = linkStatus == null || linkStatus.isEnabled();
|
||||
|
||||
//noinspection CodeBlock2Expr
|
||||
RequestConfirmationDialog.showDeny(requireContext(), requestingMember.getRequester(), linkEnabled, () -> {
|
||||
viewModel.denyRequestFor(requestingMember);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
|
||||
super.onActivityCreated(savedInstanceState);
|
||||
|
||||
@Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
|
||||
GroupId.V2 groupId = GroupId.parseOrThrow(Objects.requireNonNull(requireArguments().getString(GROUP_ID))).requireV2();
|
||||
|
||||
RequestingMemberInvitesViewModel.Factory factory = new RequestingMemberInvitesViewModel.Factory(requireContext(), groupId);
|
||||
|
||||
viewModel = ViewModelProviders.of(requireActivity(), factory).get(RequestingMemberInvitesViewModel.class);
|
||||
viewModel = new ViewModelProvider(this, factory).get(RequestingMemberInvitesViewModel.class);
|
||||
|
||||
viewModel.getRequesting().observe(getViewLifecycleOwner(), requesting -> {
|
||||
requestingMembers.setMembers(requesting);
|
||||
noRequestingMessage.setVisibility(requesting.isEmpty() ? View.VISIBLE: View.GONE);
|
||||
noRequestingMessage.setVisibility(requesting.isEmpty() ? View.VISIBLE : View.GONE);
|
||||
requestingExplanation.setVisibility(requesting.isEmpty() ? View.GONE : View.VISIBLE);
|
||||
});
|
||||
|
||||
|
|
|
@ -101,7 +101,7 @@ class ReviewCardRepository {
|
|||
|
||||
SignalExecutors.BOUNDED.execute(() -> {
|
||||
try {
|
||||
GroupManager.ejectFromGroup(context, groupId, reviewCard.getReviewRecipient());
|
||||
GroupManager.ejectAndBanFromGroup(context, groupId, reviewCard.getReviewRecipient());
|
||||
onRemoveFromGroupListener.onActionCompleted();
|
||||
} catch (GroupChangeException | IOException e) {
|
||||
onRemoveFromGroupListener.onActionFailed();
|
||||
|
|
|
@ -290,6 +290,10 @@ public final class RecipientBottomSheetDialogFragment extends BottomSheetDialogF
|
|||
makeGroupAdminButton.setVisibility(adminStatus.isCanMakeAdmin() ? View.VISIBLE : View.GONE);
|
||||
removeAdminButton.setVisibility(adminStatus.isCanMakeNonAdmin() ? View.VISIBLE : View.GONE);
|
||||
removeFromGroupButton.setVisibility(adminStatus.isCanRemove() ? View.VISIBLE : View.GONE);
|
||||
|
||||
if (adminStatus.isCanRemove()) {
|
||||
removeFromGroupButton.setOnClickListener(view -> viewModel.onRemoveFromGroupClicked(requireActivity(), adminStatus.isLinkActive(), this::dismiss));
|
||||
}
|
||||
});
|
||||
|
||||
viewModel.getIdentity().observe(getViewLifecycleOwner(), identityRecord -> {
|
||||
|
@ -319,8 +323,6 @@ public final class RecipientBottomSheetDialogFragment extends BottomSheetDialogF
|
|||
makeGroupAdminButton.setOnClickListener(view -> viewModel.onMakeGroupAdminClicked(requireActivity()));
|
||||
removeAdminButton.setOnClickListener(view -> viewModel.onRemoveGroupAdminClicked(requireActivity()));
|
||||
|
||||
removeFromGroupButton.setOnClickListener(view -> viewModel.onRemoveFromGroupClicked(requireActivity(), this::dismiss));
|
||||
|
||||
addToGroupButton.setOnClickListener(view -> {
|
||||
dismiss();
|
||||
viewModel.onAddToGroupButton(requireActivity());
|
||||
|
|
|
@ -77,7 +77,7 @@ final class RecipientDialogRepository {
|
|||
SimpleTask.run(SignalExecutors.UNBOUNDED,
|
||||
() -> {
|
||||
try {
|
||||
GroupManager.ejectFromGroup(context, Objects.requireNonNull(groupId).requireV2(), Recipient.resolved(recipientId));
|
||||
GroupManager.ejectAndBanFromGroup(context, Objects.requireNonNull(groupId).requireV2(), Recipient.resolved(recipientId));
|
||||
return true;
|
||||
} catch (GroupChangeException | IOException e) {
|
||||
Log.w(TAG, e);
|
||||
|
|
|
@ -15,6 +15,8 @@ import androidx.lifecycle.Transformations;
|
|||
import androidx.lifecycle.ViewModel;
|
||||
import androidx.lifecycle.ViewModelProvider;
|
||||
|
||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
|
||||
|
||||
import org.signal.core.util.ThreadUtil;
|
||||
import org.thoughtcrime.securesms.BlockUnblockDialog;
|
||||
import org.thoughtcrime.securesms.R;
|
||||
|
@ -33,6 +35,7 @@ import org.thoughtcrime.securesms.recipients.RecipientUtil;
|
|||
import org.thoughtcrime.securesms.util.CommunicationActions;
|
||||
import org.thoughtcrime.securesms.util.livedata.LiveDataUtil;
|
||||
import org.thoughtcrime.securesms.verify.VerifyIdentityActivity;
|
||||
import org.whispersystems.libsignal.util.Pair;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
|
@ -68,20 +71,22 @@ final class RecipientDialogViewModel extends ViewModel {
|
|||
if (recipientDialogRepository.getGroupId() != null && recipientDialogRepository.getGroupId().isV2() && !recipientIsSelf) {
|
||||
LiveGroup source = new LiveGroup(recipientDialogRepository.getGroupId());
|
||||
|
||||
LiveData<Boolean> localIsAdmin = source.isSelfAdmin();
|
||||
LiveData<Pair<Boolean, Boolean>> localStatus = LiveDataUtil.combineLatest(source.isSelfAdmin(), Transformations.map(source.getGroupLink(), s -> s == null || s.isEnabled()), Pair::new);
|
||||
LiveData<GroupDatabase.MemberLevel> recipientMemberLevel = Transformations.switchMap(recipient, source::getMemberLevel);
|
||||
|
||||
adminActionStatus = LiveDataUtil.combineLatest(localIsAdmin, recipientMemberLevel,
|
||||
(localAdmin, memberLevel) -> {
|
||||
boolean inGroup = memberLevel.isInGroup();
|
||||
boolean recipientAdmin = memberLevel == GroupDatabase.MemberLevel.ADMINISTRATOR;
|
||||
adminActionStatus = LiveDataUtil.combineLatest(localStatus, recipientMemberLevel, (statuses, memberLevel) -> {
|
||||
boolean localAdmin = statuses.first();
|
||||
boolean isLinkActive = statuses.second();
|
||||
boolean inGroup = memberLevel.isInGroup();
|
||||
boolean recipientAdmin = memberLevel == GroupDatabase.MemberLevel.ADMINISTRATOR;
|
||||
|
||||
return new AdminActionStatus(inGroup && localAdmin,
|
||||
inGroup && localAdmin && !recipientAdmin,
|
||||
inGroup && localAdmin && recipientAdmin);
|
||||
});
|
||||
return new AdminActionStatus(inGroup && localAdmin,
|
||||
inGroup && localAdmin && !recipientAdmin,
|
||||
inGroup && localAdmin && recipientAdmin,
|
||||
isLinkActive);
|
||||
});
|
||||
} else {
|
||||
adminActionStatus = new MutableLiveData<>(new AdminActionStatus(false, false, false));
|
||||
adminActionStatus = new MutableLiveData<>(new AdminActionStatus(false, false, false, false));
|
||||
}
|
||||
|
||||
boolean isSelf = recipientDialogRepository.getRecipientId().equals(Recipient.self().getId());
|
||||
|
@ -164,7 +169,7 @@ final class RecipientDialogViewModel extends ViewModel {
|
|||
}
|
||||
|
||||
void onMakeGroupAdminClicked(@NonNull Activity activity) {
|
||||
new AlertDialog.Builder(activity)
|
||||
new MaterialAlertDialogBuilder(activity)
|
||||
.setMessage(context.getString(R.string.RecipientBottomSheet_s_will_be_able_to_edit_group, Objects.requireNonNull(recipient.getValue()).getDisplayName(context)))
|
||||
.setPositiveButton(R.string.RecipientBottomSheet_make_admin,
|
||||
(dialog, which) -> {
|
||||
|
@ -182,7 +187,7 @@ final class RecipientDialogViewModel extends ViewModel {
|
|||
}
|
||||
|
||||
void onRemoveGroupAdminClicked(@NonNull Activity activity) {
|
||||
new AlertDialog.Builder(activity)
|
||||
new MaterialAlertDialogBuilder(activity)
|
||||
.setMessage(context.getString(R.string.RecipientBottomSheet_remove_s_as_group_admin, Objects.requireNonNull(recipient.getValue()).getDisplayName(context)))
|
||||
.setPositiveButton(R.string.RecipientBottomSheet_remove_as_admin,
|
||||
(dialog, which) -> {
|
||||
|
@ -199,9 +204,11 @@ final class RecipientDialogViewModel extends ViewModel {
|
|||
.show();
|
||||
}
|
||||
|
||||
void onRemoveFromGroupClicked(@NonNull Activity activity, @NonNull Runnable onSuccess) {
|
||||
new AlertDialog.Builder(activity)
|
||||
.setMessage(context.getString(R.string.RecipientBottomSheet_remove_s_from_the_group, Objects.requireNonNull(recipient.getValue()).getDisplayName(context)))
|
||||
void onRemoveFromGroupClicked(@NonNull Activity activity, boolean isLinkActive, @NonNull Runnable onSuccess) {
|
||||
new MaterialAlertDialogBuilder(activity)
|
||||
.setMessage(context.getString(isLinkActive ? R.string.RecipientBottomSheet_remove_s_from_the_group_they_will_not_be_able_to_rejoin
|
||||
: R.string.RecipientBottomSheet_remove_s_from_the_group,
|
||||
Objects.requireNonNull(recipient.getValue()).getDisplayName(context)))
|
||||
.setPositiveButton(R.string.RecipientBottomSheet_remove,
|
||||
(dialog, which) -> {
|
||||
adminActionBusy.setValue(true);
|
||||
|
@ -234,11 +241,13 @@ final class RecipientDialogViewModel extends ViewModel {
|
|||
private final boolean canRemove;
|
||||
private final boolean canMakeAdmin;
|
||||
private final boolean canMakeNonAdmin;
|
||||
private final boolean isLinkActive;
|
||||
|
||||
AdminActionStatus(boolean canRemove, boolean canMakeAdmin, boolean canMakeNonAdmin) {
|
||||
AdminActionStatus(boolean canRemove, boolean canMakeAdmin, boolean canMakeNonAdmin, boolean isLinkActive) {
|
||||
this.canRemove = canRemove;
|
||||
this.canMakeAdmin = canMakeAdmin;
|
||||
this.canMakeNonAdmin = canMakeNonAdmin;
|
||||
this.isLinkActive = isLinkActive;
|
||||
}
|
||||
|
||||
boolean isCanRemove() {
|
||||
|
@ -252,6 +261,10 @@ final class RecipientDialogViewModel extends ViewModel {
|
|||
boolean isCanMakeNonAdmin() {
|
||||
return canMakeNonAdmin;
|
||||
}
|
||||
|
||||
boolean isLinkActive() {
|
||||
return isLinkActive;
|
||||
}
|
||||
}
|
||||
|
||||
public static class Factory implements ViewModelProvider.Factory {
|
||||
|
|
|
@ -913,6 +913,8 @@
|
|||
<!-- GV2 Request confirmation dialog -->
|
||||
<string name="RequestConfirmationDialog_add_s_to_the_group">Add “%1$s” to the group?</string>
|
||||
<string name="RequestConfirmationDialog_deny_request_from_s">Deny request from “%1$s”?</string>
|
||||
<!-- Confirm dialog message shown when deny a group link join request and group link is enabled. -->
|
||||
<string name="RequestConfirmationDialog_deny_request_from_s_they_will_not_be_able_to_request">Deny request from “%1$s”? They will not be able to request to join via the group link again.</string>
|
||||
<string name="RequestConfirmationDialog_add">Add</string>
|
||||
<string name="RequestConfirmationDialog_deny">Deny</string>
|
||||
|
||||
|
@ -3350,6 +3352,8 @@
|
|||
<string name="RecipientBottomSheet_s_will_be_able_to_edit_group">"%1$s" will be able to edit this group and its members.</string>
|
||||
|
||||
<string name="RecipientBottomSheet_remove_s_from_the_group">Remove %1$s from the group?</string>
|
||||
<!-- Dialog message shown when removing someone from a group with group link being active to indicate they will not be able to rejoin -->
|
||||
<string name="RecipientBottomSheet_remove_s_from_the_group_they_will_not_be_able_to_rejoin">Remove %1$s from the group? They will not be able to rejoin via the group link.</string>
|
||||
<string name="RecipientBottomSheet_remove">Remove</string>
|
||||
<string name="RecipientBottomSheet_copied_to_clipboard">Copied to clipboard</string>
|
||||
|
||||
|
|
|
@ -40,4 +40,8 @@ public interface ChangeSetModifier {
|
|||
void clearModifyDescription();
|
||||
|
||||
void clearModifyAnnouncementsOnly();
|
||||
|
||||
void removeAddBannedMembers(int i);
|
||||
|
||||
void removeDeleteBannedMembers(int i);
|
||||
}
|
||||
|
|
|
@ -133,6 +133,16 @@ final class DecryptedGroupChangeActionsBuilderChangeSetModifier implements Chang
|
|||
result.clearNewIsAnnouncementGroup();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAddBannedMembers(int i) {
|
||||
result.removeNewBannedMembers(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeDeleteBannedMembers(int i) {
|
||||
result.removeDeleteBannedMembers(i);
|
||||
}
|
||||
|
||||
private static List<ByteString> removeIndexFromByteStringList(List<ByteString> byteStrings, int i) {
|
||||
List<ByteString> modifiedList = new ArrayList<>(byteStrings);
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ import com.google.protobuf.ByteString;
|
|||
import org.signal.storageservice.protos.groups.AccessControl;
|
||||
import org.signal.storageservice.protos.groups.Member;
|
||||
import org.signal.storageservice.protos.groups.local.DecryptedApproveMember;
|
||||
import org.signal.storageservice.protos.groups.local.DecryptedBannedMember;
|
||||
import org.signal.storageservice.protos.groups.local.DecryptedGroup;
|
||||
import org.signal.storageservice.protos.groups.local.DecryptedGroupChange;
|
||||
import org.signal.storageservice.protos.groups.local.DecryptedMember;
|
||||
|
@ -297,6 +298,10 @@ public final class DecryptedGroupUtil {
|
|||
|
||||
applyInviteLinkPassword(builder, change);
|
||||
|
||||
applyAddBannedMembersActions(builder, change.getNewBannedMembersList());
|
||||
|
||||
applyDeleteBannedMembersActions(builder, change.getDeleteBannedMembersList());
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
|
@ -505,6 +510,31 @@ public final class DecryptedGroupUtil {
|
|||
}
|
||||
}
|
||||
|
||||
private static void applyAddBannedMembersActions(DecryptedGroup.Builder builder, List<DecryptedBannedMember> newBannedMembersList) {
|
||||
Set<ByteString> bannedMemberUuidSet = getBannedMemberUuidSet(builder.getBannedMembersList());
|
||||
|
||||
for (DecryptedBannedMember member : newBannedMembersList) {
|
||||
if (bannedMemberUuidSet.contains(member.getUuid())) {
|
||||
Log.w(TAG, "Banned member already in banned list");
|
||||
} else {
|
||||
builder.addBannedMembers(member);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void applyDeleteBannedMembersActions(DecryptedGroup.Builder builder, List<DecryptedBannedMember> deleteMembersList) {
|
||||
for (DecryptedBannedMember removedMember : deleteMembersList) {
|
||||
int index = indexOfUuidInBannedMemberList(builder.getBannedMembersList(), removedMember.getUuid());
|
||||
|
||||
if (index == -1) {
|
||||
Log.w(TAG, "Deleted banned member on change not found in banned list");
|
||||
continue;
|
||||
}
|
||||
|
||||
builder.removeBannedMembers(index);
|
||||
}
|
||||
}
|
||||
|
||||
private static DecryptedMember withNewProfileKey(DecryptedMember member, ByteString profileKey) {
|
||||
return DecryptedMember.newBuilder(member)
|
||||
.setProfileKey(profileKey)
|
||||
|
@ -531,6 +561,16 @@ public final class DecryptedGroupUtil {
|
|||
return pendingMemberCipherTexts;
|
||||
}
|
||||
|
||||
private static Set<ByteString> getBannedMemberUuidSet(List<DecryptedBannedMember> bannedMemberList) {
|
||||
Set<ByteString> memberUuids = new HashSet<>(bannedMemberList.size());
|
||||
|
||||
for (DecryptedBannedMember member : bannedMemberList) {
|
||||
memberUuids.add(member.getUuid());
|
||||
}
|
||||
|
||||
return memberUuids;
|
||||
}
|
||||
|
||||
private static void removePendingAndRequestingMembersNowInGroup(DecryptedGroup.Builder builder) {
|
||||
Set<ByteString> allMembers = membersToUuidByteStringSet(builder.getMembersList());
|
||||
|
||||
|
@ -569,6 +609,13 @@ public final class DecryptedGroupUtil {
|
|||
return -1;
|
||||
}
|
||||
|
||||
private static int indexOfUuidInBannedMemberList(List<DecryptedBannedMember> memberList, ByteString uuid) {
|
||||
for (int i = 0; i < memberList.size(); i++) {
|
||||
if (uuid.equals(memberList.get(i).getUuid())) return i;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static Optional<UUID> findInviter(List<DecryptedPendingMember> pendingMembersList, UUID uuid) {
|
||||
return Optional.fromNullable(findPendingByUuid(pendingMembersList, uuid).transform(DecryptedPendingMember::getAddedByUuid)
|
||||
.transform(UuidUtil::fromByteStringOrNull)
|
||||
|
@ -598,7 +645,9 @@ public final class DecryptedGroupUtil {
|
|||
change.getPromoteRequestingMembersCount() == 0 && // field 18
|
||||
change.getNewInviteLinkPassword().size() == 0 && // field 19
|
||||
!change.hasNewDescription() && // field 20
|
||||
isEmpty(change.getNewIsAnnouncementGroup()); // field 20
|
||||
isEmpty(change.getNewIsAnnouncementGroup()) && // field 21
|
||||
change.getNewBannedMembersCount() == 0 && // field 22
|
||||
change.getDeleteBannedMembersCount() == 0; // field 23
|
||||
}
|
||||
|
||||
static boolean isEmpty(AccessControl.AccessRequired newAttributeAccess) {
|
||||
|
|
|
@ -112,4 +112,14 @@ final class GroupChangeActionsBuilderChangeSetModifier implements ChangeSetModif
|
|||
public void clearModifyAnnouncementsOnly() {
|
||||
result.clearModifyAnnouncementsOnly();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeAddBannedMembers(int i) {
|
||||
result.removeAddBannedMembers(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeDeleteBannedMembers(int i) {
|
||||
result.removeDeleteBannedMembers(i);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package org.whispersystems.signalservice.api.groupsv2;
|
|||
import com.google.protobuf.ByteString;
|
||||
|
||||
import org.signal.storageservice.protos.groups.local.DecryptedApproveMember;
|
||||
import org.signal.storageservice.protos.groups.local.DecryptedBannedMember;
|
||||
import org.signal.storageservice.protos.groups.local.DecryptedGroup;
|
||||
import org.signal.storageservice.protos.groups.local.DecryptedGroupChange;
|
||||
import org.signal.storageservice.protos.groups.local.DecryptedMember;
|
||||
|
@ -65,12 +66,17 @@ public final class GroupChangeReconstruct {
|
|||
Set<ByteString> requestingMembersListA = requestingMembersToSetOfUuids(fromState.getRequestingMembersList());
|
||||
Set<ByteString> requestingMembersListB = requestingMembersToSetOfUuids(toState.getRequestingMembersList());
|
||||
|
||||
Set<ByteString> bannedMembersListA = bannedMembersToSetOfUuids(fromState.getBannedMembersList());
|
||||
Set<ByteString> bannedMembersListB = bannedMembersToSetOfUuids(toState.getBannedMembersList());
|
||||
|
||||
Set<ByteString> removedPendingMemberUuids = subtract(pendingMembersListA, pendingMembersListB);
|
||||
Set<ByteString> removedRequestingMemberUuids = subtract(requestingMembersListA, requestingMembersListB);
|
||||
Set<ByteString> newPendingMemberUuids = subtract(pendingMembersListB, pendingMembersListA);
|
||||
Set<ByteString> newRequestingMemberUuids = subtract(requestingMembersListB, requestingMembersListA);
|
||||
Set<ByteString> removedMemberUuids = subtract(fromStateMemberUuids, toStateMemberUuids);
|
||||
Set<ByteString> newMemberUuids = subtract(toStateMemberUuids, fromStateMemberUuids);
|
||||
Set<ByteString> removedBannedMemberUuids = subtract(bannedMembersListA, bannedMembersListB);
|
||||
Set<ByteString> newBannedMemberUuids = subtract(bannedMembersListB, bannedMembersListA);
|
||||
|
||||
Set<ByteString> addedByInvitationUuids = intersect(newMemberUuids, removedPendingMemberUuids);
|
||||
Set<ByteString> addedByRequestApprovalUuids = intersect(newMemberUuids, removedRequestingMemberUuids);
|
||||
|
@ -141,6 +147,14 @@ public final class GroupChangeReconstruct {
|
|||
builder.setNewInviteLinkPassword(toState.getInviteLinkPassword());
|
||||
}
|
||||
|
||||
for (ByteString uuid : removedBannedMemberUuids) {
|
||||
builder.addDeleteBannedMembers(DecryptedBannedMember.newBuilder().setUuid(uuid).build());
|
||||
}
|
||||
|
||||
for (ByteString uuid : newBannedMemberUuids) {
|
||||
builder.addNewBannedMembers(DecryptedBannedMember.newBuilder().setUuid(uuid).build());
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
|
@ -203,6 +217,14 @@ public final class GroupChangeReconstruct {
|
|||
return uuids;
|
||||
}
|
||||
|
||||
private static Set<ByteString> bannedMembersToSetOfUuids(Collection<DecryptedBannedMember> bannedMembers) {
|
||||
Set<ByteString> uuids = new LinkedHashSet<>(bannedMembers.size());
|
||||
for (DecryptedBannedMember bannedMember : bannedMembers) {
|
||||
uuids.add(bannedMember.getUuid());
|
||||
}
|
||||
return uuids;
|
||||
}
|
||||
|
||||
private static <T> Set<T> subtract(Collection<T> a, Collection<T> b) {
|
||||
Set<T> result = new LinkedHashSet<>(a);
|
||||
result.removeAll(b);
|
||||
|
|
|
@ -4,6 +4,7 @@ import com.google.protobuf.ByteString;
|
|||
|
||||
import org.signal.storageservice.protos.groups.GroupChange;
|
||||
import org.signal.storageservice.protos.groups.local.DecryptedApproveMember;
|
||||
import org.signal.storageservice.protos.groups.local.DecryptedBannedMember;
|
||||
import org.signal.storageservice.protos.groups.local.DecryptedGroup;
|
||||
import org.signal.storageservice.protos.groups.local.DecryptedGroupChange;
|
||||
import org.signal.storageservice.protos.groups.local.DecryptedMember;
|
||||
|
@ -43,7 +44,9 @@ public final class GroupChangeUtil {
|
|||
change.getPromoteRequestingMembersCount() == 0 && // field 18
|
||||
!change.hasModifyInviteLinkPassword() && // field 19
|
||||
!change.hasModifyDescription() && // field 20
|
||||
!change.hasModifyAnnouncementsOnly(); // field 21
|
||||
!change.hasModifyAnnouncementsOnly() && // field 21
|
||||
change.getAddBannedMembersCount() == 0 && // field 22
|
||||
change.getDeleteBannedMembersCount() == 0; // field 23
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -107,6 +110,7 @@ public final class GroupChangeUtil {
|
|||
HashMap<ByteString, DecryptedMember> fullMembersByUuid = new HashMap<>(groupState.getMembersCount());
|
||||
HashMap<ByteString, DecryptedPendingMember> pendingMembersByUuid = new HashMap<>(groupState.getPendingMembersCount());
|
||||
HashMap<ByteString, DecryptedRequestingMember> requestingMembersByUuid = new HashMap<>(groupState.getMembersCount());
|
||||
HashMap<ByteString, DecryptedBannedMember> bannedMembersByUuid = new HashMap<>(groupState.getBannedMembersCount());
|
||||
|
||||
for (DecryptedMember member : groupState.getMembersList()) {
|
||||
fullMembersByUuid.put(member.getUuid(), member);
|
||||
|
@ -120,6 +124,10 @@ public final class GroupChangeUtil {
|
|||
requestingMembersByUuid.put(member.getUuid(), member);
|
||||
}
|
||||
|
||||
for (DecryptedBannedMember member : groupState.getBannedMembersList()) {
|
||||
bannedMembersByUuid.put(member.getUuid(), member);
|
||||
}
|
||||
|
||||
resolveField3AddMembers (conflictingChange, changeSetModifier, fullMembersByUuid, pendingMembersByUuid);
|
||||
resolveField4DeleteMembers (conflictingChange, changeSetModifier, fullMembersByUuid);
|
||||
resolveField5ModifyMemberRoles (conflictingChange, changeSetModifier, fullMembersByUuid);
|
||||
|
@ -138,6 +146,8 @@ public final class GroupChangeUtil {
|
|||
resolveField18PromoteRequestingMembers (conflictingChange, changeSetModifier, requestingMembersByUuid);
|
||||
resolveField20ModifyDescription (groupState, conflictingChange, changeSetModifier);
|
||||
resolveField21ModifyAnnouncementsOnly (groupState, conflictingChange, changeSetModifier);
|
||||
resolveField22AddBannedMembers (conflictingChange, changeSetModifier, bannedMembersByUuid);
|
||||
resolveField23DeleteBannedMembers (conflictingChange, changeSetModifier, bannedMembersByUuid);
|
||||
}
|
||||
|
||||
private static void resolveField3AddMembers(DecryptedGroupChange conflictingChange, ChangeSetModifier result, HashMap<ByteString, DecryptedMember> fullMembersByUuid, HashMap<ByteString, DecryptedPendingMember> pendingMembersByUuid) {
|
||||
|
@ -319,4 +329,28 @@ public final class GroupChangeUtil {
|
|||
result.clearModifyAnnouncementsOnly();
|
||||
}
|
||||
}
|
||||
|
||||
private static void resolveField22AddBannedMembers(DecryptedGroupChange conflictingChange, ChangeSetModifier result, HashMap<ByteString, DecryptedBannedMember> bannedMembersByUuid) {
|
||||
List<DecryptedBannedMember> newBannedMembersList = conflictingChange.getNewBannedMembersList();
|
||||
|
||||
for (int i = newBannedMembersList.size() - 1; i >= 0; i--) {
|
||||
DecryptedBannedMember member = newBannedMembersList.get(i);
|
||||
|
||||
if (bannedMembersByUuid.containsKey(member.getUuid())) {
|
||||
result.removeAddBannedMembers(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void resolveField23DeleteBannedMembers(DecryptedGroupChange conflictingChange, ChangeSetModifier result, HashMap<ByteString, DecryptedBannedMember> bannedMembersByUuid) {
|
||||
List<DecryptedBannedMember> deleteBannedMembersList = conflictingChange.getDeleteBannedMembersList();
|
||||
|
||||
for (int i = deleteBannedMembersList.size() - 1; i >= 0; i--) {
|
||||
DecryptedBannedMember member = deleteBannedMembersList.get(i);
|
||||
|
||||
if (!bannedMembersByUuid.containsKey(member.getUuid())) {
|
||||
result.removeDeleteBannedMembers(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import com.google.protobuf.ByteString;
|
|||
import com.google.protobuf.InvalidProtocolBufferException;
|
||||
|
||||
import org.signal.storageservice.protos.groups.AccessControl;
|
||||
import org.signal.storageservice.protos.groups.BannedMember;
|
||||
import org.signal.storageservice.protos.groups.Group;
|
||||
import org.signal.storageservice.protos.groups.GroupAttributeBlob;
|
||||
import org.signal.storageservice.protos.groups.GroupChange;
|
||||
|
@ -12,6 +13,7 @@ import org.signal.storageservice.protos.groups.Member;
|
|||
import org.signal.storageservice.protos.groups.PendingMember;
|
||||
import org.signal.storageservice.protos.groups.RequestingMember;
|
||||
import org.signal.storageservice.protos.groups.local.DecryptedApproveMember;
|
||||
import org.signal.storageservice.protos.groups.local.DecryptedBannedMember;
|
||||
import org.signal.storageservice.protos.groups.local.DecryptedGroup;
|
||||
import org.signal.storageservice.protos.groups.local.DecryptedGroupChange;
|
||||
import org.signal.storageservice.protos.groups.local.DecryptedGroupJoinInfo;
|
||||
|
@ -47,6 +49,7 @@ import java.util.List;
|
|||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Contains operations to create, modify and validate groups and group changes.
|
||||
|
@ -59,7 +62,7 @@ public final class GroupsV2Operations {
|
|||
public static final UUID UNKNOWN_UUID = UuidUtil.UNKNOWN_UUID;
|
||||
|
||||
/** Highest change epoch this class knows now to decrypt */
|
||||
public static final int HIGHEST_KNOWN_EPOCH = 3;
|
||||
public static final int HIGHEST_KNOWN_EPOCH = 4;
|
||||
|
||||
private final ServerPublicParams serverPublicParams;
|
||||
private final ClientZkProfileOperations clientZkProfileOperations;
|
||||
|
@ -160,7 +163,7 @@ public final class GroupsV2Operations {
|
|||
public GroupChange.Actions.Builder createModifyGroupMembershipChange(Set<GroupCandidate> membersToAdd, UUID selfUuid) {
|
||||
final GroupOperations groupOperations = forGroup(groupSecretParams);
|
||||
|
||||
GroupChange.Actions.Builder actions = GroupChange.Actions.newBuilder();
|
||||
GroupChange.Actions.Builder actions = createUnbanUuidsChange(membersToAdd.stream().map(GroupCandidate::getUuid).collect(Collectors.toSet()));
|
||||
|
||||
for (GroupCandidate credential : membersToAdd) {
|
||||
Member.Role newMemberRole = Member.Role.DEFAULT;
|
||||
|
@ -203,8 +206,9 @@ public final class GroupsV2Operations {
|
|||
return actions;
|
||||
}
|
||||
|
||||
public GroupChange.Actions.Builder createRefuseGroupJoinRequest(Set<UUID> requestsToRemove) {
|
||||
GroupChange.Actions.Builder actions = GroupChange.Actions.newBuilder();
|
||||
public GroupChange.Actions.Builder createRefuseGroupJoinRequest(Set<UUID> requestsToRemove, boolean alsoBan) {
|
||||
GroupChange.Actions.Builder actions = alsoBan ? createBanUuidsChange(requestsToRemove)
|
||||
: GroupChange.Actions.newBuilder();
|
||||
|
||||
for (UUID uuid : requestsToRemove) {
|
||||
actions.addDeleteRequestingMembers(GroupChange.Actions.DeleteRequestingMemberAction
|
||||
|
@ -228,8 +232,9 @@ public final class GroupsV2Operations {
|
|||
return actions;
|
||||
}
|
||||
|
||||
public GroupChange.Actions.Builder createRemoveMembersChange(final Set<UUID> membersToRemove) {
|
||||
GroupChange.Actions.Builder actions = GroupChange.Actions.newBuilder();
|
||||
public GroupChange.Actions.Builder createRemoveMembersChange(final Set<UUID> membersToRemove, boolean alsoBan) {
|
||||
GroupChange.Actions.Builder actions = alsoBan ? createBanUuidsChange(membersToRemove)
|
||||
: GroupChange.Actions.newBuilder();
|
||||
|
||||
for (UUID remove: membersToRemove) {
|
||||
actions.addDeleteMembers(GroupChange.Actions.DeleteMemberAction
|
||||
|
@ -241,7 +246,7 @@ public final class GroupsV2Operations {
|
|||
}
|
||||
|
||||
public GroupChange.Actions.Builder createLeaveAndPromoteMembersToAdmin(UUID self, List<UUID> membersToMakeAdmin) {
|
||||
GroupChange.Actions.Builder actions = createRemoveMembersChange(Collections.singleton(self));
|
||||
GroupChange.Actions.Builder actions = createRemoveMembersChange(Collections.singleton(self), false);
|
||||
|
||||
for (UUID member : membersToMakeAdmin) {
|
||||
actions.addModifyMemberRoles(GroupChange.Actions.ModifyMemberRoleAction
|
||||
|
@ -342,6 +347,26 @@ public final class GroupsV2Operations {
|
|||
.setAnnouncementsOnly(isAnnouncementGroup));
|
||||
}
|
||||
|
||||
public GroupChange.Actions.Builder createBanUuidsChange(Set<UUID> banUuids) {
|
||||
GroupChange.Actions.Builder builder = GroupChange.Actions.newBuilder();
|
||||
|
||||
for (UUID uuid : banUuids) {
|
||||
builder.addAddBannedMembers(GroupChange.Actions.AddBannedMemberAction.newBuilder().setAdded(BannedMember.newBuilder().setUserId(encryptUuid(uuid)).build()));
|
||||
}
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
public GroupChange.Actions.Builder createUnbanUuidsChange(Set<UUID> banUuids) {
|
||||
GroupChange.Actions.Builder builder = GroupChange.Actions.newBuilder();
|
||||
|
||||
for (UUID uuid : banUuids) {
|
||||
builder.addDeleteBannedMembers(GroupChange.Actions.DeleteBannedMemberAction.newBuilder().setDeletedUserId(encryptUuid(uuid)).build());
|
||||
}
|
||||
|
||||
return builder;
|
||||
}
|
||||
|
||||
private Member.Builder member(ProfileKeyCredential credential, Member.Role role) {
|
||||
ProfileKeyCredentialPresentation presentation = clientZkProfileOperations.createProfileKeyCredentialPresentation(new SecureRandom(), groupSecretParams, credential);
|
||||
|
||||
|
@ -410,6 +435,7 @@ public final class GroupsV2Operations {
|
|||
List<DecryptedMember> decryptedMembers = new ArrayList<>(membersList.size());
|
||||
List<DecryptedPendingMember> decryptedPendingMembers = new ArrayList<>(pendingMembersList.size());
|
||||
List<DecryptedRequestingMember> decryptedRequestingMembers = new ArrayList<>(requestingMembersList.size());
|
||||
List<DecryptedBannedMember> decryptedBannedMembers = new ArrayList<>(group.getBannedMembersCount());
|
||||
|
||||
for (Member member : membersList) {
|
||||
try {
|
||||
|
@ -427,6 +453,10 @@ public final class GroupsV2Operations {
|
|||
decryptedRequestingMembers.add(decryptRequestingMember(member));
|
||||
}
|
||||
|
||||
for (BannedMember member : group.getBannedMembersList()) {
|
||||
decryptedBannedMembers.add(DecryptedBannedMember.newBuilder().setUuid(decryptUuidToByteString(member.getUserId())).build());
|
||||
}
|
||||
|
||||
return DecryptedGroup.newBuilder()
|
||||
.setTitle(decryptTitle(group.getTitle()))
|
||||
.setDescription(decryptDescription(group.getDescription()))
|
||||
|
@ -439,6 +469,7 @@ public final class GroupsV2Operations {
|
|||
.addAllRequestingMembers(decryptedRequestingMembers)
|
||||
.setDisappearingMessagesTimer(DecryptedTimer.newBuilder().setDuration(decryptDisappearingMessagesTimer(group.getDisappearingMessagesTimer())))
|
||||
.setInviteLinkPassword(group.getInviteLinkPassword())
|
||||
.addAllBannedMembers(decryptedBannedMembers)
|
||||
.build();
|
||||
}
|
||||
|
||||
|
@ -625,6 +656,16 @@ public final class GroupsV2Operations {
|
|||
builder.setNewIsAnnouncementGroup(actions.getModifyAnnouncementsOnly().getAnnouncementsOnly() ? EnabledState.ENABLED : EnabledState.DISABLED);
|
||||
}
|
||||
|
||||
// Field 22
|
||||
for (GroupChange.Actions.AddBannedMemberAction action : actions.getAddBannedMembersList()) {
|
||||
builder.addNewBannedMembers(DecryptedBannedMember.newBuilder().setUuid(decryptUuidToByteString(action.getAdded().getUserId())).build());
|
||||
}
|
||||
|
||||
// Field 23
|
||||
for (GroupChange.Actions.DeleteBannedMemberAction action : actions.getDeleteBannedMembersList()) {
|
||||
builder.addDeleteBannedMembers(DecryptedBannedMember.newBuilder().setUuid(decryptUuidToByteString(action.getDeletedUserId())).build());
|
||||
}
|
||||
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
|
|
|
@ -33,6 +33,10 @@ message DecryptedRequestingMember {
|
|||
uint64 timestamp = 4;
|
||||
}
|
||||
|
||||
message DecryptedBannedMember {
|
||||
bytes uuid = 1;
|
||||
}
|
||||
|
||||
message DecryptedPendingMemberRemoval {
|
||||
bytes uuid = 1;
|
||||
bytes uuidCipherText = 2;
|
||||
|
@ -62,6 +66,7 @@ message DecryptedGroup {
|
|||
bytes inviteLinkPassword = 10;
|
||||
string description = 11;
|
||||
EnabledState isAnnouncementGroup = 12;
|
||||
repeated DecryptedBannedMember bannedMembers = 13;
|
||||
}
|
||||
|
||||
// Decrypted version of message GroupChange.Actions
|
||||
|
@ -88,6 +93,8 @@ message DecryptedGroupChange {
|
|||
bytes newInviteLinkPassword = 19;
|
||||
DecryptedString newDescription = 20;
|
||||
EnabledState newIsAnnouncementGroup = 21;
|
||||
repeated DecryptedBannedMember newBannedMembers = 22;
|
||||
repeated DecryptedBannedMember deleteBannedMembers = 23;
|
||||
}
|
||||
|
||||
message DecryptedString {
|
||||
|
|
|
@ -45,6 +45,10 @@ message RequestingMember {
|
|||
uint64 timestamp = 4;
|
||||
}
|
||||
|
||||
message BannedMember {
|
||||
bytes userId = 1;
|
||||
}
|
||||
|
||||
message AccessControl {
|
||||
enum AccessRequired {
|
||||
UNKNOWN = 0;
|
||||
|
@ -72,6 +76,7 @@ message Group {
|
|||
bytes inviteLinkPassword = 10;
|
||||
bytes description = 11;
|
||||
bool announcementsOnly = 12;
|
||||
repeated BannedMember bannedMembers = 13;
|
||||
}
|
||||
|
||||
message GroupChange {
|
||||
|
@ -121,6 +126,14 @@ message GroupChange {
|
|||
Member.Role role = 2;
|
||||
}
|
||||
|
||||
message AddBannedMemberAction {
|
||||
BannedMember added = 1;
|
||||
}
|
||||
|
||||
message DeleteBannedMemberAction {
|
||||
bytes deletedUserId = 1;
|
||||
}
|
||||
|
||||
message ModifyTitleAction {
|
||||
bytes title = 1;
|
||||
}
|
||||
|
@ -178,6 +191,8 @@ message GroupChange {
|
|||
ModifyInviteLinkPasswordAction modifyInviteLinkPassword = 19;
|
||||
ModifyDescriptionAction modifyDescription = 20;
|
||||
ModifyAnnouncementsOnlyAction modifyAnnouncementsOnly = 21;
|
||||
repeated AddBannedMemberAction addBannedMembers = 22;
|
||||
repeated DeleteBannedMemberAction deleteBannedMembers = 23;
|
||||
}
|
||||
|
||||
bytes actions = 1;
|
||||
|
|
|
@ -6,6 +6,7 @@ import org.junit.Test;
|
|||
import org.signal.storageservice.protos.groups.AccessControl;
|
||||
import org.signal.storageservice.protos.groups.Member;
|
||||
import org.signal.storageservice.protos.groups.local.DecryptedApproveMember;
|
||||
import org.signal.storageservice.protos.groups.local.DecryptedBannedMember;
|
||||
import org.signal.storageservice.protos.groups.local.DecryptedGroup;
|
||||
import org.signal.storageservice.protos.groups.local.DecryptedGroupChange;
|
||||
import org.signal.storageservice.protos.groups.local.DecryptedMember;
|
||||
|
@ -26,6 +27,7 @@ import static org.junit.Assert.assertEquals;
|
|||
import static org.whispersystems.signalservice.api.groupsv2.ProtoTestUtils.admin;
|
||||
import static org.whispersystems.signalservice.api.groupsv2.ProtoTestUtils.asAdmin;
|
||||
import static org.whispersystems.signalservice.api.groupsv2.ProtoTestUtils.asMember;
|
||||
import static org.whispersystems.signalservice.api.groupsv2.ProtoTestUtils.bannedMember;
|
||||
import static org.whispersystems.signalservice.api.groupsv2.ProtoTestUtils.member;
|
||||
import static org.whispersystems.signalservice.api.groupsv2.ProtoTestUtils.newProfileKey;
|
||||
import static org.whispersystems.signalservice.api.groupsv2.ProtoTestUtils.pendingMember;
|
||||
|
@ -46,7 +48,7 @@ public final class DecryptedGroupUtil_apply_Test {
|
|||
int maxFieldFound = getMaxDeclaredFieldNumber(DecryptedGroupChange.class);
|
||||
|
||||
assertEquals("DecryptedGroupUtil and its tests need updating to account for new fields on " + DecryptedGroupChange.class.getName(),
|
||||
21, maxFieldFound);
|
||||
23, maxFieldFound);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -885,4 +887,75 @@ public final class DecryptedGroupUtil_apply_Test {
|
|||
newGroup);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Test
|
||||
public void apply_new_banned_member() throws NotAbleToApplyGroupV2ChangeException {
|
||||
DecryptedMember member1 = member(UUID.randomUUID());
|
||||
DecryptedBannedMember banned = bannedMember(UUID.randomUUID());
|
||||
|
||||
DecryptedGroup newGroup = DecryptedGroupUtil.apply(DecryptedGroup.newBuilder()
|
||||
.setRevision(10)
|
||||
.addMembers(member1)
|
||||
.build(),
|
||||
DecryptedGroupChange.newBuilder()
|
||||
.setRevision(11)
|
||||
.addNewBannedMembers(banned)
|
||||
.build());
|
||||
|
||||
assertEquals(DecryptedGroup.newBuilder()
|
||||
.setRevision(11)
|
||||
.addMembers(member1)
|
||||
.addBannedMembers(banned)
|
||||
.build(),
|
||||
newGroup);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void apply_new_banned_member_already_banned() throws NotAbleToApplyGroupV2ChangeException {
|
||||
DecryptedMember member1 = member(UUID.randomUUID());
|
||||
DecryptedBannedMember banned = bannedMember(UUID.randomUUID());
|
||||
|
||||
DecryptedGroup newGroup = DecryptedGroupUtil.apply(DecryptedGroup.newBuilder()
|
||||
.setRevision(10)
|
||||
.addMembers(member1)
|
||||
.addBannedMembers(banned)
|
||||
.build(),
|
||||
DecryptedGroupChange.newBuilder()
|
||||
.setRevision(11)
|
||||
.addNewBannedMembers(banned)
|
||||
.build());
|
||||
|
||||
assertEquals(DecryptedGroup.newBuilder()
|
||||
.setRevision(11)
|
||||
.addMembers(member1)
|
||||
.addBannedMembers(banned)
|
||||
.build(),
|
||||
newGroup);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void remove_banned_member() throws NotAbleToApplyGroupV2ChangeException {
|
||||
DecryptedMember member1 = member(UUID.randomUUID());
|
||||
UUID bannedUuid = UUID.randomUUID();
|
||||
DecryptedBannedMember banned = bannedMember(bannedUuid);
|
||||
|
||||
DecryptedGroup newGroup = DecryptedGroupUtil.apply(DecryptedGroup.newBuilder()
|
||||
.setRevision(10)
|
||||
.addMembers(member1)
|
||||
.addBannedMembers(banned)
|
||||
.build(),
|
||||
DecryptedGroupChange.newBuilder()
|
||||
.setRevision(11)
|
||||
.addDeleteBannedMembers(DecryptedBannedMember.newBuilder()
|
||||
.setUuid(UuidUtil.toByteString(bannedUuid))
|
||||
.build())
|
||||
.build());
|
||||
|
||||
assertEquals(DecryptedGroup.newBuilder()
|
||||
.setRevision(11)
|
||||
.addMembers(member1)
|
||||
.build(),
|
||||
newGroup);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import com.google.protobuf.ByteString;
|
|||
import org.junit.Test;
|
||||
import org.signal.storageservice.protos.groups.AccessControl;
|
||||
import org.signal.storageservice.protos.groups.local.DecryptedApproveMember;
|
||||
import org.signal.storageservice.protos.groups.local.DecryptedBannedMember;
|
||||
import org.signal.storageservice.protos.groups.local.DecryptedGroupChange;
|
||||
import org.signal.storageservice.protos.groups.local.DecryptedRequestingMember;
|
||||
import org.signal.storageservice.protos.groups.local.DecryptedString;
|
||||
|
@ -36,7 +37,7 @@ public final class DecryptedGroupUtil_empty_Test {
|
|||
int maxFieldFound = getMaxDeclaredFieldNumber(DecryptedGroupChange.class);
|
||||
|
||||
assertEquals("DecryptedGroupUtil and its tests need updating to account for new fields on " + DecryptedGroupChange.class.getName(),
|
||||
21, maxFieldFound);
|
||||
23, maxFieldFound);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -233,4 +234,24 @@ public final class DecryptedGroupUtil_empty_Test {
|
|||
assertFalse(DecryptedGroupUtil.changeIsEmpty(change));
|
||||
assertFalse(DecryptedGroupUtil.changeIsEmptyExceptForProfileKeyChanges(change));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void not_empty_with_add_banned_member_field_22() {
|
||||
DecryptedGroupChange change = DecryptedGroupChange.newBuilder()
|
||||
.addNewBannedMembers(DecryptedBannedMember.getDefaultInstance())
|
||||
.build();
|
||||
|
||||
assertFalse(DecryptedGroupUtil.changeIsEmpty(change));
|
||||
assertFalse(DecryptedGroupUtil.changeIsEmptyExceptForProfileKeyChanges(change));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void not_empty_with_delete_banned_member_field_23() {
|
||||
DecryptedGroupChange change = DecryptedGroupChange.newBuilder()
|
||||
.addDeleteBannedMembers(DecryptedBannedMember.getDefaultInstance())
|
||||
.build();
|
||||
|
||||
assertFalse(DecryptedGroupUtil.changeIsEmpty(change));
|
||||
assertFalse(DecryptedGroupUtil.changeIsEmptyExceptForProfileKeyChanges(change));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ import static org.junit.Assert.assertEquals;
|
|||
import static org.whispersystems.signalservice.api.groupsv2.ProtoTestUtils.admin;
|
||||
import static org.whispersystems.signalservice.api.groupsv2.ProtoTestUtils.approveAdmin;
|
||||
import static org.whispersystems.signalservice.api.groupsv2.ProtoTestUtils.approveMember;
|
||||
import static org.whispersystems.signalservice.api.groupsv2.ProtoTestUtils.bannedMember;
|
||||
import static org.whispersystems.signalservice.api.groupsv2.ProtoTestUtils.demoteAdmin;
|
||||
import static org.whispersystems.signalservice.api.groupsv2.ProtoTestUtils.member;
|
||||
import static org.whispersystems.signalservice.api.groupsv2.ProtoTestUtils.newProfileKey;
|
||||
|
@ -42,7 +43,7 @@ public final class GroupChangeReconstructTest {
|
|||
int maxFieldFound = getMaxDeclaredFieldNumber(DecryptedGroup.class);
|
||||
|
||||
assertEquals("GroupChangeReconstruct and its tests need updating to account for new fields on " + DecryptedGroup.class.getName(),
|
||||
12, maxFieldFound);
|
||||
13, maxFieldFound);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -381,4 +382,26 @@ public final class GroupChangeReconstructTest {
|
|||
.build(),
|
||||
decryptedGroupChange);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void new_banned_member() {
|
||||
UUID uuidNew = UUID.randomUUID();
|
||||
DecryptedGroup from = DecryptedGroup.newBuilder().build();
|
||||
DecryptedGroup to = DecryptedGroup.newBuilder().addBannedMembers(bannedMember(uuidNew)).build();
|
||||
|
||||
DecryptedGroupChange decryptedGroupChange = GroupChangeReconstruct.reconstructGroupChange(from, to);
|
||||
|
||||
assertEquals(DecryptedGroupChange.newBuilder().addNewBannedMembers(bannedMember(uuidNew)).build(), decryptedGroupChange);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removed_banned_member() {
|
||||
UUID uuidOld = UUID.randomUUID();
|
||||
DecryptedGroup from = DecryptedGroup.newBuilder().addBannedMembers(bannedMember(uuidOld)).build();
|
||||
DecryptedGroup to = DecryptedGroup.newBuilder().build();
|
||||
|
||||
DecryptedGroupChange decryptedGroupChange = GroupChangeReconstruct.reconstructGroupChange(from, to);
|
||||
|
||||
assertEquals(DecryptedGroupChange.newBuilder().addDeleteBannedMembers(bannedMember(uuidOld)).build(), decryptedGroupChange);
|
||||
}
|
||||
}
|
|
@ -20,7 +20,7 @@ public final class GroupChangeUtil_changeIsEmpty_Test {
|
|||
int maxFieldFound = getMaxDeclaredFieldNumber(GroupChange.Actions.class);
|
||||
|
||||
assertEquals("GroupChangeUtil and its tests need updating to account for new fields on " + GroupChange.Actions.class.getName(),
|
||||
21, maxFieldFound);
|
||||
23, maxFieldFound);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -198,4 +198,22 @@ public final class GroupChangeUtil_changeIsEmpty_Test {
|
|||
|
||||
assertFalse(GroupChangeUtil.changeIsEmpty(actions));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void not_empty_with_add_banned_member_field_22() {
|
||||
GroupChange.Actions actions = GroupChange.Actions.newBuilder()
|
||||
.addAddBannedMembers(GroupChange.Actions.AddBannedMemberAction.getDefaultInstance())
|
||||
.build();
|
||||
|
||||
assertFalse(GroupChangeUtil.changeIsEmpty(actions));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void not_empty_with_delete_banned_member_field_23() {
|
||||
GroupChange.Actions actions = GroupChange.Actions.newBuilder()
|
||||
.addDeleteBannedMembers(GroupChange.Actions.DeleteBannedMemberAction.getDefaultInstance())
|
||||
.build();
|
||||
|
||||
assertFalse(GroupChangeUtil.changeIsEmpty(actions));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import com.google.protobuf.ByteString;
|
|||
|
||||
import org.junit.Test;
|
||||
import org.signal.storageservice.protos.groups.AccessControl;
|
||||
import org.signal.storageservice.protos.groups.BannedMember;
|
||||
import org.signal.storageservice.protos.groups.GroupChange;
|
||||
import org.signal.storageservice.protos.groups.Member;
|
||||
import org.signal.storageservice.protos.groups.PendingMember;
|
||||
|
@ -22,6 +23,7 @@ import static org.junit.Assert.assertEquals;
|
|||
import static org.junit.Assert.assertTrue;
|
||||
import static org.whispersystems.signalservice.api.groupsv2.ProtoTestUtils.admin;
|
||||
import static org.whispersystems.signalservice.api.groupsv2.ProtoTestUtils.approveMember;
|
||||
import static org.whispersystems.signalservice.api.groupsv2.ProtoTestUtils.bannedMember;
|
||||
import static org.whispersystems.signalservice.api.groupsv2.ProtoTestUtils.demoteAdmin;
|
||||
import static org.whispersystems.signalservice.api.groupsv2.ProtoTestUtils.encrypt;
|
||||
import static org.whispersystems.signalservice.api.groupsv2.ProtoTestUtils.encryptedMember;
|
||||
|
@ -47,7 +49,7 @@ public final class GroupChangeUtil_resolveConflict_Test {
|
|||
int maxFieldFound = getMaxDeclaredFieldNumber(DecryptedGroupChange.class);
|
||||
|
||||
assertEquals("GroupChangeUtil#resolveConflict and its tests need updating to account for new fields on " + DecryptedGroupChange.class.getName(),
|
||||
21, maxFieldFound);
|
||||
23, maxFieldFound);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -60,7 +62,7 @@ public final class GroupChangeUtil_resolveConflict_Test {
|
|||
int maxFieldFound = getMaxDeclaredFieldNumber(DecryptedGroupChange.class);
|
||||
|
||||
assertEquals("GroupChangeUtil#resolveConflict and its tests need updating to account for new fields on " + GroupChange.class.getName(),
|
||||
21, maxFieldFound);
|
||||
23, maxFieldFound);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -73,7 +75,7 @@ public final class GroupChangeUtil_resolveConflict_Test {
|
|||
int maxFieldFound = getMaxDeclaredFieldNumber(DecryptedGroup.class);
|
||||
|
||||
assertEquals("GroupChangeUtil#resolveConflict and its tests need updating to account for new fields on " + DecryptedGroup.class.getName(),
|
||||
12, maxFieldFound);
|
||||
13, maxFieldFound);
|
||||
}
|
||||
|
||||
|
||||
|
@ -741,4 +743,63 @@ public final class GroupChangeUtil_resolveConflict_Test {
|
|||
|
||||
assertTrue(GroupChangeUtil.changeIsEmpty(resolvedActions));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void field_22__add_banned_members() {
|
||||
UUID member1 = UUID.randomUUID();
|
||||
UUID member2 = UUID.randomUUID();
|
||||
UUID member3 = UUID.randomUUID();
|
||||
DecryptedGroup groupState = DecryptedGroup.newBuilder()
|
||||
.addMembers(member(member1))
|
||||
.addBannedMembers(bannedMember(member3))
|
||||
.build();
|
||||
DecryptedGroupChange decryptedChange = DecryptedGroupChange.newBuilder()
|
||||
.addNewBannedMembers(bannedMember(member1))
|
||||
.addNewBannedMembers(bannedMember(member2))
|
||||
.addNewBannedMembers(bannedMember(member3))
|
||||
.build();
|
||||
|
||||
GroupChange.Actions change = GroupChange.Actions.newBuilder()
|
||||
.addAddBannedMembers(GroupChange.Actions.AddBannedMemberAction.newBuilder().setAdded(BannedMember.newBuilder().setUserId(encrypt(member1))))
|
||||
.addAddBannedMembers(GroupChange.Actions.AddBannedMemberAction.newBuilder().setAdded(BannedMember.newBuilder().setUserId(encrypt(member2))))
|
||||
.addAddBannedMembers(GroupChange.Actions.AddBannedMemberAction.newBuilder().setAdded(BannedMember.newBuilder().setUserId(encrypt(member3))))
|
||||
.build();
|
||||
|
||||
GroupChange.Actions resolvedActions = GroupChangeUtil.resolveConflict(groupState, decryptedChange, change).build();
|
||||
|
||||
GroupChange.Actions expected = GroupChange.Actions.newBuilder()
|
||||
.addAddBannedMembers(GroupChange.Actions.AddBannedMemberAction.newBuilder().setAdded(BannedMember.newBuilder().setUserId(encrypt(member1))))
|
||||
.addAddBannedMembers(GroupChange.Actions.AddBannedMemberAction.newBuilder().setAdded(BannedMember.newBuilder().setUserId(encrypt(member2))))
|
||||
.build();
|
||||
assertEquals(expected, resolvedActions);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void field_23__delete_banned_members() {
|
||||
UUID member1 = UUID.randomUUID();
|
||||
UUID member2 = UUID.randomUUID();
|
||||
UUID member3 = UUID.randomUUID();
|
||||
DecryptedGroup groupState = DecryptedGroup.newBuilder()
|
||||
.addMembers(member(member1))
|
||||
.addBannedMembers(bannedMember(member2))
|
||||
.build();
|
||||
DecryptedGroupChange decryptedChange = DecryptedGroupChange.newBuilder()
|
||||
.addDeleteBannedMembers(bannedMember(member1))
|
||||
.addDeleteBannedMembers(bannedMember(member2))
|
||||
.addDeleteBannedMembers(bannedMember(member3))
|
||||
.build();
|
||||
GroupChange.Actions change = GroupChange.Actions.newBuilder()
|
||||
.addDeleteBannedMembers(GroupChange.Actions.DeleteBannedMemberAction.newBuilder().setDeletedUserId(encrypt(member1)))
|
||||
.addDeleteBannedMembers(GroupChange.Actions.DeleteBannedMemberAction.newBuilder().setDeletedUserId(encrypt(member2)))
|
||||
.addDeleteBannedMembers(GroupChange.Actions.DeleteBannedMemberAction.newBuilder().setDeletedUserId(encrypt(member3)))
|
||||
.build();
|
||||
|
||||
GroupChange.Actions resolvedActions = GroupChangeUtil.resolveConflict(groupState, decryptedChange, change).build();
|
||||
|
||||
GroupChange.Actions expected = GroupChange.Actions.newBuilder()
|
||||
.addDeleteBannedMembers(GroupChange.Actions.DeleteBannedMemberAction.newBuilder().setDeletedUserId(encrypt(member2)))
|
||||
.build();
|
||||
assertEquals(expected, resolvedActions);
|
||||
}
|
||||
}
|
|
@ -19,6 +19,7 @@ import static org.junit.Assert.assertEquals;
|
|||
import static org.junit.Assert.assertTrue;
|
||||
import static org.whispersystems.signalservice.api.groupsv2.ProtoTestUtils.admin;
|
||||
import static org.whispersystems.signalservice.api.groupsv2.ProtoTestUtils.approveMember;
|
||||
import static org.whispersystems.signalservice.api.groupsv2.ProtoTestUtils.bannedMember;
|
||||
import static org.whispersystems.signalservice.api.groupsv2.ProtoTestUtils.demoteAdmin;
|
||||
import static org.whispersystems.signalservice.api.groupsv2.ProtoTestUtils.member;
|
||||
import static org.whispersystems.signalservice.api.groupsv2.ProtoTestUtils.pendingMember;
|
||||
|
@ -40,7 +41,7 @@ public final class GroupChangeUtil_resolveConflict_decryptedOnly_Test {
|
|||
int maxFieldFound = getMaxDeclaredFieldNumber(DecryptedGroupChange.class);
|
||||
|
||||
assertEquals("GroupChangeUtil#resolveConflict and its tests need updating to account for new fields on " + DecryptedGroupChange.class.getName(),
|
||||
21, maxFieldFound);
|
||||
23, maxFieldFound);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -53,7 +54,7 @@ public final class GroupChangeUtil_resolveConflict_decryptedOnly_Test {
|
|||
int maxFieldFound = getMaxDeclaredFieldNumber(DecryptedGroup.class);
|
||||
|
||||
assertEquals("GroupChangeUtil#resolveConflict and its tests need updating to account for new fields on " + DecryptedGroup.class.getName(),
|
||||
12, maxFieldFound);
|
||||
13, maxFieldFound);
|
||||
}
|
||||
|
||||
|
||||
|
@ -600,4 +601,53 @@ public final class GroupChangeUtil_resolveConflict_decryptedOnly_Test {
|
|||
|
||||
assertTrue(DecryptedGroupUtil.changeIsEmpty(resolvedChanges));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void field_22__add_banned_members() {
|
||||
UUID member1 = UUID.randomUUID();
|
||||
UUID member2 = UUID.randomUUID();
|
||||
UUID member3 = UUID.randomUUID();
|
||||
DecryptedGroup groupState = DecryptedGroup.newBuilder()
|
||||
.addMembers(member(member1))
|
||||
.addBannedMembers(bannedMember(member3))
|
||||
.build();
|
||||
DecryptedGroupChange decryptedChange = DecryptedGroupChange.newBuilder()
|
||||
.addNewBannedMembers(bannedMember(member1))
|
||||
.addNewBannedMembers(bannedMember(member2))
|
||||
.addNewBannedMembers(bannedMember(member3))
|
||||
.build();
|
||||
|
||||
DecryptedGroupChange resolvedChanges = GroupChangeUtil.resolveConflict(groupState, decryptedChange).build();
|
||||
|
||||
DecryptedGroupChange expected = DecryptedGroupChange.newBuilder()
|
||||
.addNewBannedMembers(bannedMember(member1))
|
||||
.addNewBannedMembers(bannedMember(member2))
|
||||
.build();
|
||||
|
||||
assertEquals(expected, resolvedChanges);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void field_23__delete_banned_members() {
|
||||
UUID member1 = UUID.randomUUID();
|
||||
UUID member2 = UUID.randomUUID();
|
||||
UUID member3 = UUID.randomUUID();
|
||||
DecryptedGroup groupState = DecryptedGroup.newBuilder()
|
||||
.addMembers(member(member1))
|
||||
.addBannedMembers(bannedMember(member2))
|
||||
.build();
|
||||
DecryptedGroupChange decryptedChange = DecryptedGroupChange.newBuilder()
|
||||
.addDeleteBannedMembers(bannedMember(member1))
|
||||
.addDeleteBannedMembers(bannedMember(member2))
|
||||
.addDeleteBannedMembers(bannedMember(member3))
|
||||
.build();
|
||||
|
||||
DecryptedGroupChange resolvedChanges = GroupChangeUtil.resolveConflict(groupState, decryptedChange).build();
|
||||
|
||||
DecryptedGroupChange expected = DecryptedGroupChange.newBuilder()
|
||||
.addDeleteBannedMembers(bannedMember(member2))
|
||||
.build();
|
||||
|
||||
assertEquals(expected, resolvedChanges);
|
||||
}
|
||||
}
|
|
@ -9,6 +9,7 @@ import org.signal.storageservice.protos.groups.AccessControl;
|
|||
import org.signal.storageservice.protos.groups.GroupChange;
|
||||
import org.signal.storageservice.protos.groups.Member;
|
||||
import org.signal.storageservice.protos.groups.local.DecryptedApproveMember;
|
||||
import org.signal.storageservice.protos.groups.local.DecryptedBannedMember;
|
||||
import org.signal.storageservice.protos.groups.local.DecryptedGroupChange;
|
||||
import org.signal.storageservice.protos.groups.local.DecryptedMember;
|
||||
import org.signal.storageservice.protos.groups.local.DecryptedModifyMemberRole;
|
||||
|
@ -65,7 +66,7 @@ public final class GroupsV2Operations_decrypt_change_Test {
|
|||
int maxFieldFound = getMaxDeclaredFieldNumber(DecryptedGroupChange.class);
|
||||
|
||||
assertEquals("GroupV2Operations#decryptChange and its tests need updating to account for new fields on " + DecryptedGroupChange.class.getName(),
|
||||
21, maxFieldFound);
|
||||
23, maxFieldFound);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -102,7 +103,8 @@ public final class GroupsV2Operations_decrypt_change_Test {
|
|||
.setRole(Member.Role.DEFAULT)
|
||||
.setProfileKey(ByteString.copyFrom(profileKey.serialize()))
|
||||
.setJoinedAtRevision(10)
|
||||
.setUuid(UuidUtil.toByteString(newMember))));
|
||||
.setUuid(UuidUtil.toByteString(newMember)))
|
||||
.addDeleteBannedMembers(DecryptedBannedMember.newBuilder().setUuid(UuidUtil.toByteString(newMember)).build()));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -137,7 +139,8 @@ public final class GroupsV2Operations_decrypt_change_Test {
|
|||
.setRole(Member.Role.DEFAULT)
|
||||
.setProfileKey(ByteString.copyFrom(profileKey.serialize()))
|
||||
.setJoinedAtRevision(10)
|
||||
.setUuid(UuidUtil.toByteString(newMember))));
|
||||
.setUuid(UuidUtil.toByteString(newMember)))
|
||||
.addDeleteBannedMembers(DecryptedBannedMember.newBuilder().setUuid(UuidUtil.toByteString(newMember)).build()));
|
||||
}
|
||||
|
||||
@Test(expected = InvalidGroupStateException.class)
|
||||
|
@ -156,7 +159,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))
|
||||
assertDecryption(groupOperations.createRemoveMembersChange(Collections.singleton(oldMember), false)
|
||||
.setRevision(10),
|
||||
DecryptedGroupChange.newBuilder()
|
||||
.setRevision(10)
|
||||
|
@ -227,7 +230,8 @@ public final class GroupsV2Operations_decrypt_change_Test {
|
|||
.setAddedByUuid(UuidUtil.toByteString(self))
|
||||
.setUuidCipherText(groupOperations.encryptUuid(newMember))
|
||||
.setRole(Member.Role.DEFAULT)
|
||||
.setUuid(UuidUtil.toByteString(newMember))));
|
||||
.setUuid(UuidUtil.toByteString(newMember)))
|
||||
.addDeleteBannedMembers(DecryptedBannedMember.newBuilder().setUuid(UuidUtil.toByteString(newMember)).build()));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -340,11 +344,12 @@ 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))
|
||||
assertDecryption(groupOperations.createRefuseGroupJoinRequest(Collections.singleton(newRequestingMember), true)
|
||||
.setRevision(10),
|
||||
DecryptedGroupChange.newBuilder()
|
||||
.setRevision(10)
|
||||
.addDeleteRequestingMembers(UuidUtil.toByteString(newRequestingMember)));
|
||||
.addDeleteRequestingMembers(UuidUtil.toByteString(newRequestingMember))
|
||||
.addNewBannedMembers(DecryptedBannedMember.newBuilder().setUuid(UuidUtil.toByteString(newRequestingMember)).build()));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -387,6 +392,30 @@ public final class GroupsV2Operations_decrypt_change_Test {
|
|||
.setNewIsAnnouncementGroup(EnabledState.ENABLED));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void can_decrypt_member_bans_field22() {
|
||||
UUID ban = UUID.randomUUID();
|
||||
|
||||
assertDecryption(groupOperations.createBanUuidsChange(Collections.singleton(ban))
|
||||
.setRevision(13),
|
||||
DecryptedGroupChange.newBuilder()
|
||||
.setRevision(13)
|
||||
.addNewBannedMembers(DecryptedBannedMember.newBuilder()
|
||||
.setUuid(UuidUtil.toByteString(ban))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void can_decrypt_banned_member_removals_field23() {
|
||||
UUID ban = UUID.randomUUID();
|
||||
|
||||
assertDecryption(groupOperations.createUnbanUuidsChange(Collections.singleton(ban))
|
||||
.setRevision(13),
|
||||
DecryptedGroupChange.newBuilder()
|
||||
.setRevision(13)
|
||||
.addDeleteBannedMembers(DecryptedBannedMember.newBuilder()
|
||||
.setUuid(UuidUtil.toByteString(ban))));
|
||||
}
|
||||
|
||||
private static ProfileKey newProfileKey() {
|
||||
try {
|
||||
return new ProfileKey(Util.getSecretBytes(32));
|
||||
|
|
|
@ -6,12 +6,14 @@ import com.google.protobuf.InvalidProtocolBufferException;
|
|||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.signal.storageservice.protos.groups.AccessControl;
|
||||
import org.signal.storageservice.protos.groups.BannedMember;
|
||||
import org.signal.storageservice.protos.groups.Group;
|
||||
import org.signal.storageservice.protos.groups.GroupChange;
|
||||
import org.signal.storageservice.protos.groups.Member;
|
||||
import org.signal.storageservice.protos.groups.PendingMember;
|
||||
import org.signal.storageservice.protos.groups.RequestingMember;
|
||||
import org.signal.storageservice.protos.groups.local.DecryptedApproveMember;
|
||||
import org.signal.storageservice.protos.groups.local.DecryptedBannedMember;
|
||||
import org.signal.storageservice.protos.groups.local.DecryptedGroup;
|
||||
import org.signal.storageservice.protos.groups.local.DecryptedGroupChange;
|
||||
import org.signal.storageservice.protos.groups.local.DecryptedMember;
|
||||
|
@ -75,7 +77,7 @@ public final class GroupsV2Operations_decrypt_group_Test {
|
|||
int maxFieldFound = getMaxDeclaredFieldNumber(Group.class);
|
||||
|
||||
assertEquals("GroupOperations and its tests need updating to account for new fields on " + Group.class.getName(),
|
||||
12, maxFieldFound);
|
||||
13, maxFieldFound);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -295,6 +297,20 @@ public final class GroupsV2Operations_decrypt_group_Test {
|
|||
assertEquals(EnabledState.ENABLED, decryptedGroup.getIsAnnouncementGroup());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void decrypt_banned_members_field_13() throws VerificationFailedException, InvalidGroupStateException {
|
||||
UUID member1 = UUID.randomUUID();
|
||||
|
||||
Group group = Group.newBuilder()
|
||||
.addBannedMembers(BannedMember.newBuilder().setUserId(groupOperations.encryptUuid(member1)))
|
||||
.build();
|
||||
|
||||
DecryptedGroup decryptedGroup = groupOperations.decryptGroup(group);
|
||||
|
||||
assertEquals(1, decryptedGroup.getBannedMembersCount());
|
||||
assertEquals(DecryptedBannedMember.newBuilder().setUuid(UuidUtil.toByteString(member1)).build(), decryptedGroup.getBannedMembers(0));
|
||||
}
|
||||
|
||||
private ByteString encryptProfileKey(UUID uuid, ProfileKey profileKey) {
|
||||
return ByteString.copyFrom(new ClientZkGroupCipher(groupSecretParams).encryptProfileKey(profileKey, uuid).serialize());
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@ import com.google.protobuf.ByteString;
|
|||
import org.signal.storageservice.protos.groups.Member;
|
||||
import org.signal.storageservice.protos.groups.RequestingMember;
|
||||
import org.signal.storageservice.protos.groups.local.DecryptedApproveMember;
|
||||
import org.signal.storageservice.protos.groups.local.DecryptedBannedMember;
|
||||
import org.signal.storageservice.protos.groups.local.DecryptedMember;
|
||||
import org.signal.storageservice.protos.groups.local.DecryptedModifyMemberRole;
|
||||
import org.signal.storageservice.protos.groups.local.DecryptedPendingMember;
|
||||
|
@ -122,6 +123,12 @@ final class ProtoTestUtils {
|
|||
.build();
|
||||
}
|
||||
|
||||
static DecryptedBannedMember bannedMember(UUID uuid) {
|
||||
return DecryptedBannedMember.newBuilder()
|
||||
.setUuid(UuidUtil.toByteString(uuid))
|
||||
.build();
|
||||
}
|
||||
|
||||
static DecryptedApproveMember approveMember(UUID uuid) {
|
||||
return approve(uuid, Member.Role.DEFAULT);
|
||||
}
|
||||
|
|
Ładowanie…
Reference in New Issue