Implement add/remove admin privileges

This commit is contained in:
AsamK 2021-05-15 11:08:01 +02:00
parent b972522d74
commit 3de30e166f
7 changed files with 104 additions and 15 deletions

View file

@ -831,6 +831,8 @@ public class Manager implements Closeable {
String description,
List<String> members,
List<String> removeMembers,
List<String> admins,
List<String> removeAdmins,
File avatarFile
) throws IOException, GroupNotFoundException, AttachmentInvalidException, InvalidNumberException, NotAGroupMemberException {
return updateGroup(groupId,
@ -838,21 +840,32 @@ public class Manager implements Closeable {
description,
members == null ? null : getSignalServiceAddresses(members),
removeMembers == null ? null : getSignalServiceAddresses(removeMembers),
admins == null ? null : getSignalServiceAddresses(admins),
removeAdmins == null ? null : getSignalServiceAddresses(removeAdmins),
avatarFile);
}
private Pair<Long, List<SendMessageResult>> updateGroup(
GroupId groupId,
String name,
String description,
Set<RecipientId> members,
final GroupId groupId,
final String name,
final String description,
final Set<RecipientId> members,
final Set<RecipientId> removeMembers,
File avatarFile
final Set<RecipientId> admins,
final Set<RecipientId> removeAdmins,
final File avatarFile
) throws IOException, GroupNotFoundException, AttachmentInvalidException, NotAGroupMemberException {
var group = getGroupForUpdating(groupId);
if (group instanceof GroupInfoV2) {
return updateGroupV2((GroupInfoV2) group, name, description, members, removeMembers, avatarFile);
return updateGroupV2((GroupInfoV2) group,
name,
description,
members,
removeMembers,
admins,
removeAdmins,
avatarFile);
}
return updateGroupV1((GroupInfoV1) group, name, members, avatarFile);
@ -913,6 +926,8 @@ public class Manager implements Closeable {
final String description,
final Set<RecipientId> members,
final Set<RecipientId> removeMembers,
final Set<RecipientId> admins,
final Set<RecipientId> removeAdmins,
final File avatarFile
) throws IOException {
Pair<Long, List<SendMessageResult>> result = null;
@ -947,6 +962,33 @@ public class Manager implements Closeable {
}
}
if (admins != null) {
final var newAdmins = new HashSet<>(admins);
newAdmins.retainAll(group.getMembers());
newAdmins.removeAll(group.getAdminMembers());
if (newAdmins.size() > 0) {
for (var admin : newAdmins) {
var groupGroupChangePair = groupV2Helper.setMemberAdmin(group, admin, true);
result = sendUpdateGroupV2Message(group,
groupGroupChangePair.first(),
groupGroupChangePair.second());
}
}
}
if (removeAdmins != null) {
final var existingRemoveAdmins = new HashSet<>(removeAdmins);
existingRemoveAdmins.retainAll(group.getAdminMembers());
if (existingRemoveAdmins.size() > 0) {
for (var admin : existingRemoveAdmins) {
var groupGroupChangePair = groupV2Helper.setMemberAdmin(group, admin, false);
result = sendUpdateGroupV2Message(group,
groupGroupChangePair.first(),
groupGroupChangePair.second());
}
}
}
if (result == null || name != null || description != null || avatarFile != null) {
var groupGroupChangePair = groupV2Helper.updateGroup(group, name, description, avatarFile);
if (avatarFile != null) {

View file

@ -227,8 +227,7 @@ public class GroupV2Helper {
public Pair<DecryptedGroup, GroupChange> addMembers(
GroupInfoV2 groupInfoV2, Set<RecipientId> newMembers
) throws IOException {
final var groupSecretParams = GroupSecretParams.deriveFromMasterKey(groupInfoV2.getMasterKey());
var groupOperations = groupsV2Operations.forGroup(groupSecretParams);
GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2);
if (!areMembersValid(newMembers)) {
throw new IOException("Failed to update group");
@ -318,8 +317,7 @@ public class GroupV2Helper {
}
public Pair<DecryptedGroup, GroupChange> acceptInvite(GroupInfoV2 groupInfoV2) throws IOException {
final var groupSecretParams = GroupSecretParams.deriveFromMasterKey(groupInfoV2.getMasterKey());
final var groupOperations = groupsV2Operations.forGroup(groupSecretParams);
final GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2);
final var selfRecipientId = this.selfRecipientIdProvider.getSelfRecipientId();
final var profileKeyCredential = profileKeyCredentialProvider.getProfileKeyCredential(selfRecipientId);
@ -337,11 +335,25 @@ public class GroupV2Helper {
return commitChange(groupInfoV2, change);
}
public Pair<DecryptedGroup, GroupChange> setMemberAdmin(
GroupInfoV2 groupInfoV2, RecipientId recipientId, boolean admin
) throws IOException {
final GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2);
final var address = addressResolver.resolveSignalServiceAddress(recipientId);
final var newRole = admin ? Member.Role.ADMINISTRATOR : Member.Role.DEFAULT;
final var change = groupOperations.createChangeMemberRole(address.getUuid().get(), newRole);
return commitChange(groupInfoV2, change);
}
private GroupsV2Operations.GroupOperations getGroupOperations(final GroupInfoV2 groupInfoV2) {
final var groupSecretParams = GroupSecretParams.deriveFromMasterKey(groupInfoV2.getMasterKey());
return groupsV2Operations.forGroup(groupSecretParams);
}
private Pair<DecryptedGroup, GroupChange> revokeInvites(
GroupInfoV2 groupInfoV2, Set<DecryptedPendingMember> pendingMembers
) throws IOException {
final var groupSecretParams = GroupSecretParams.deriveFromMasterKey(groupInfoV2.getMasterKey());
final var groupOperations = groupsV2Operations.forGroup(groupSecretParams);
final GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2);
final var uuidCipherTexts = pendingMembers.stream().map(member -> {
try {
return new UuidCiphertext(member.getUuidCipherText().toByteArray());
@ -355,8 +367,7 @@ public class GroupV2Helper {
private Pair<DecryptedGroup, GroupChange> ejectMembers(
GroupInfoV2 groupInfoV2, Set<UUID> uuids
) throws IOException {
final var groupSecretParams = GroupSecretParams.deriveFromMasterKey(groupInfoV2.getMasterKey());
final var groupOperations = groupsV2Operations.forGroup(groupSecretParams);
final GroupsV2Operations.GroupOperations groupOperations = getGroupOperations(groupInfoV2);
return commitChange(groupInfoV2, groupOperations.createRemoveMembersChange(uuids));
}

View file

@ -30,6 +30,10 @@ public abstract class GroupInfo {
return Set.of();
}
public Set<RecipientId> getAdminMembers() {
return Set.of();
}
public abstract boolean isBlocked();
public abstract void setBlocked(boolean blocked);

View file

@ -5,6 +5,7 @@ import org.asamk.signal.manager.groups.GroupInviteLinkUrl;
import org.asamk.signal.manager.storage.recipients.RecipientId;
import org.asamk.signal.manager.storage.recipients.RecipientResolver;
import org.signal.storageservice.protos.groups.AccessControl;
import org.signal.storageservice.protos.groups.Member;
import org.signal.storageservice.protos.groups.local.DecryptedGroup;
import org.signal.zkgroup.groups.GroupMasterKey;
import org.whispersystems.signalservice.api.push.SignalServiceAddress;
@ -116,6 +117,19 @@ public class GroupInfoV2 extends GroupInfo {
.collect(Collectors.toSet());
}
@Override
public Set<RecipientId> getAdminMembers() {
if (this.group == null) {
return Set.of();
}
return group.getMembersList()
.stream()
.filter(m -> m.getRole() == Member.Role.ADMINISTRATOR)
.map(m -> new SignalServiceAddress(UuidUtil.parseOrThrow(m.getUuid().toByteArray()), null))
.map(recipientResolver::resolveRecipient)
.collect(Collectors.toSet());
}
@Override
public boolean isBlocked() {
return blocked;

View file

@ -38,7 +38,7 @@ public class ListGroupsCommand implements LocalCommand {
final var groupInviteLink = group.getGroupInviteLink();
writer.println(
"Id: {} Name: {} Description: {} Active: {} Blocked: {} Members: {} Pending members: {} Requesting members: {} Link: {}",
"Id: {} Name: {} Description: {} Active: {} Blocked: {} Members: {} Pending members: {} Requesting members: {} Admins: {} Link: {}",
group.getGroupId().toBase64(),
group.getTitle(),
group.getDescription(),
@ -47,6 +47,7 @@ public class ListGroupsCommand implements LocalCommand {
resolveMembers(m, group.getMembers()),
resolveMembers(m, group.getPendingMembers()),
resolveMembers(m, group.getRequestingMembers()),
resolveMembers(m, group.getAdminMembers()),
groupInviteLink == null ? '-' : groupInviteLink.getUrl());
} else {
writer.println("Id: {} Name: {} Active: {} Blocked: {}",
@ -88,6 +89,7 @@ public class ListGroupsCommand implements LocalCommand {
resolveMembers(m, group.getMembers()),
resolveMembers(m, group.getPendingMembers()),
resolveMembers(m, group.getRequestingMembers()),
resolveMembers(m, group.getAdminMembers()),
groupInviteLink == null ? null : groupInviteLink.getUrl()));
}
@ -112,6 +114,7 @@ public class ListGroupsCommand implements LocalCommand {
public Set<String> members;
public Set<String> pendingMembers;
public Set<String> requestingMembers;
public Set<String> admins;
public String groupInviteLink;
public JsonGroup(
@ -123,6 +126,7 @@ public class ListGroupsCommand implements LocalCommand {
Set<String> members,
Set<String> pendingMembers,
Set<String> requestingMembers,
Set<String> admins,
String groupInviteLink
) {
this.id = id;
@ -134,6 +138,7 @@ public class ListGroupsCommand implements LocalCommand {
this.members = members;
this.pendingMembers = pendingMembers;
this.requestingMembers = requestingMembers;
this.admins = admins;
this.groupInviteLink = groupInviteLink;
}
}

View file

@ -41,6 +41,11 @@ public class UpdateGroupCommand implements DbusCommand, LocalCommand {
subparser.addArgument("-r", "--remove-member")
.nargs("*")
.help("Specify one or more members to remove from the group");
subparser.addArgument("--admin").nargs("*").help("Specify one or more members to make a group admin");
subparser.addArgument("--remove-admin")
.nargs("*")
.help("Specify one or more members to remove group admin privileges");
}
@Override
@ -64,6 +69,10 @@ public class UpdateGroupCommand implements DbusCommand, LocalCommand {
List<String> groupRemoveMembers = ns.getList("remove-member");
List<String> groupAdmins = ns.getList("admin");
List<String> groupRemoveAdmins = ns.getList("remove-admin");
var groupAvatar = ns.getString("avatar");
try {
@ -80,6 +89,8 @@ public class UpdateGroupCommand implements DbusCommand, LocalCommand {
groupDescription,
groupMembers,
groupRemoveMembers,
groupAdmins,
groupRemoveAdmins,
groupAvatar == null ? null : new File(groupAvatar));
ErrorUtils.handleTimestampAndSendMessageResults(writer, results.first(), results.second());
}

View file

@ -345,6 +345,8 @@ public class DbusSignalImpl implements Signal {
null,
members,
null,
null,
null,
avatar == null ? null : new File(avatar));
checkSendMessageResults(results.first(), results.second());
return groupId;