From: AsamK Date: Sat, 15 May 2021 09:08:01 +0000 (+0200) Subject: Implement add/remove admin privileges X-Git-Tag: v0.8.4~37 X-Git-Url: https://git.nmode.ca/signal-cli/commitdiff_plain/3de30e166f4eb6be0cd10548ed2c6cce2fe2282e?ds=sidebyside Implement add/remove admin privileges --- diff --git a/lib/src/main/java/org/asamk/signal/manager/Manager.java b/lib/src/main/java/org/asamk/signal/manager/Manager.java index cd0ce561..b003279a 100644 --- a/lib/src/main/java/org/asamk/signal/manager/Manager.java +++ b/lib/src/main/java/org/asamk/signal/manager/Manager.java @@ -831,6 +831,8 @@ public class Manager implements Closeable { String description, List members, List removeMembers, + List admins, + List 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> updateGroup( - GroupId groupId, - String name, - String description, - Set members, + final GroupId groupId, + final String name, + final String description, + final Set members, final Set removeMembers, - File avatarFile + final Set admins, + final Set 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 members, final Set removeMembers, + final Set admins, + final Set removeAdmins, final File avatarFile ) throws IOException { Pair> 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) { diff --git a/lib/src/main/java/org/asamk/signal/manager/helper/GroupV2Helper.java b/lib/src/main/java/org/asamk/signal/manager/helper/GroupV2Helper.java index 94df2a41..6dab6dab 100644 --- a/lib/src/main/java/org/asamk/signal/manager/helper/GroupV2Helper.java +++ b/lib/src/main/java/org/asamk/signal/manager/helper/GroupV2Helper.java @@ -227,8 +227,7 @@ public class GroupV2Helper { public Pair addMembers( GroupInfoV2 groupInfoV2, Set 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 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 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 revokeInvites( GroupInfoV2 groupInfoV2, Set 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 ejectMembers( GroupInfoV2 groupInfoV2, Set 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)); } diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/groups/GroupInfo.java b/lib/src/main/java/org/asamk/signal/manager/storage/groups/GroupInfo.java index e2aaff4c..211e0f96 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/groups/GroupInfo.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/groups/GroupInfo.java @@ -30,6 +30,10 @@ public abstract class GroupInfo { return Set.of(); } + public Set getAdminMembers() { + return Set.of(); + } + public abstract boolean isBlocked(); public abstract void setBlocked(boolean blocked); diff --git a/lib/src/main/java/org/asamk/signal/manager/storage/groups/GroupInfoV2.java b/lib/src/main/java/org/asamk/signal/manager/storage/groups/GroupInfoV2.java index 859c7209..308220cb 100644 --- a/lib/src/main/java/org/asamk/signal/manager/storage/groups/GroupInfoV2.java +++ b/lib/src/main/java/org/asamk/signal/manager/storage/groups/GroupInfoV2.java @@ -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 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; diff --git a/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java b/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java index 4886eac4..aab91077 100644 --- a/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java +++ b/src/main/java/org/asamk/signal/commands/ListGroupsCommand.java @@ -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 members; public Set pendingMembers; public Set requestingMembers; + public Set admins; public String groupInviteLink; public JsonGroup( @@ -123,6 +126,7 @@ public class ListGroupsCommand implements LocalCommand { Set members, Set pendingMembers, Set requestingMembers, + Set 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; } } diff --git a/src/main/java/org/asamk/signal/commands/UpdateGroupCommand.java b/src/main/java/org/asamk/signal/commands/UpdateGroupCommand.java index 513ec2e4..87307446 100644 --- a/src/main/java/org/asamk/signal/commands/UpdateGroupCommand.java +++ b/src/main/java/org/asamk/signal/commands/UpdateGroupCommand.java @@ -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 groupRemoveMembers = ns.getList("remove-member"); + List groupAdmins = ns.getList("admin"); + + List 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()); } diff --git a/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java b/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java index 6606e966..9b83ace5 100644 --- a/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java +++ b/src/main/java/org/asamk/signal/dbus/DbusSignalImpl.java @@ -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;