Skip to content

Commit b16342c

Browse files
committed
fixes #40: persist members and admins in group updates
When creating or updating a group, the provided members and admins should not be ignored. Note that this change will result in admins/members being removed, when an update to an existing group does not properly include the existing members and admins. Prior to this commit, no modification would have been made to the admins and members.
1 parent 8902fb8 commit b16342c

4 files changed

Lines changed: 98 additions & 9 deletions

File tree

changelog.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ <h1>
5252
<li>[<a href='https://github.com/igniterealtime/openfire-restAPI-plugin/issues/99'>#99</a>] - Fix hard-coded link to localhost for OpenAPI yaml link in docs.</li>
5353
<li>[<a href='https://github.com/igniterealtime/openfire-restAPI-plugin/issues/96'>#96</a>] - Don't require auth for readiness/liveness endpoints</li>
5454
<li>[<a href='https://github.com/igniterealtime/openfire-restAPI-plugin/issues/74'>#74</a>] - Return HTTP status code 404 instead of 500 when passing incorrect MUC service/room name</li>
55+
<li>[<a href='https://github.com/igniterealtime/openfire-restAPI-plugin/issues/40'>#40</a>] - When creating or updating a group, use the members and admins that are provided in the input data.</li>
5556
</ul>
5657

5758
<p><b>1.8.0</b> April 6, 2022</p>

src/java/org/jivesoftware/openfire/plugin/rest/controller/GroupController.java

Lines changed: 91 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,11 @@
1616

1717
package org.jivesoftware.openfire.plugin.rest.controller;
1818

19-
import java.util.ArrayList;
20-
import java.util.Collection;
21-
import java.util.List;
19+
import java.util.*;
2220

2321
import javax.ws.rs.core.Response;
2422

23+
import org.jivesoftware.openfire.XMPPServer;
2524
import org.jivesoftware.openfire.group.Group;
2625
import org.jivesoftware.openfire.group.GroupAlreadyExistsException;
2726
import org.jivesoftware.openfire.group.GroupManager;
@@ -30,6 +29,7 @@
3029
import org.jivesoftware.openfire.plugin.rest.exceptions.ExceptionType;
3130
import org.jivesoftware.openfire.plugin.rest.exceptions.ServiceException;
3231
import org.jivesoftware.openfire.plugin.rest.utils.MUCRoomUtils;
32+
import org.xmpp.packet.JID;
3333

3434
/**
3535
* The Class GroupController.
@@ -56,7 +56,7 @@ public static GroupController getInstance() {
5656
*/
5757
public List<GroupEntity> getGroups() throws ServiceException {
5858
Collection<Group> groups = GroupManager.getInstance().getGroups();
59-
List<GroupEntity> groupEntities = new ArrayList<GroupEntity>();
59+
List<GroupEntity> groupEntities = new ArrayList<>();
6060
for (Group group : groups) {
6161
GroupEntity groupEntity = new GroupEntity(group.getName(), group.getDescription());
6262
groupEntities.add(groupEntity);
@@ -86,7 +86,7 @@ public GroupEntity getGroup(String groupName) throws ServiceException {
8686
GroupEntity groupEntity = new GroupEntity(group.getName(), group.getDescription());
8787
groupEntity.setAdmins(MUCRoomUtils.convertJIDsToStringList(group.getAdmins()));
8888
groupEntity.setMembers(MUCRoomUtils.convertJIDsToStringList(group.getMembers()));
89-
groupEntity.setShared(group.getProperties().get("sharedRoster.showInRoster")=="onlyGroup");
89+
groupEntity.setShared("onlyGroup".equals(group.getProperties().get("sharedRoster.showInRoster")));
9090

9191
return groupEntity;
9292
}
@@ -104,6 +104,26 @@ public Group createGroup(GroupEntity groupEntity) throws ServiceException {
104104
Group group;
105105
if (groupEntity != null && !groupEntity.getName().isEmpty()) {
106106
try {
107+
final Collection<JID> newMembers = new HashSet<>();
108+
final Collection<JID> newAdmins = new HashSet<>();
109+
110+
// Input validation.
111+
for (final String newMember : groupEntity.getMembers()) {
112+
try {
113+
newMembers.add(newMember.contains("@") ? new JID(newMember) : XMPPServer.getInstance().createJID(newMember, null));
114+
} catch (IllegalArgumentException e) {
115+
throw new ServiceException("Cannot parse a member value as a JID: " + newMember, groupEntity.getName(), ExceptionType.ILLEGAL_ARGUMENT_EXCEPTION, Response.Status.BAD_REQUEST, e);
116+
}
117+
}
118+
for (final String newAdmin : groupEntity.getAdmins()) {
119+
try {
120+
newAdmins.add(newAdmin.contains("@") ? new JID(newAdmin) : XMPPServer.getInstance().createJID(newAdmin, null));
121+
} catch (IllegalArgumentException e) {
122+
throw new ServiceException("Cannot parse a admin value as a JID: " + newAdmin, groupEntity.getName(), ExceptionType.ILLEGAL_ARGUMENT_EXCEPTION, Response.Status.BAD_REQUEST, e);
123+
}
124+
}
125+
126+
// Start creating the group.
107127
group = GroupManager.getInstance().createGroup(groupEntity.getName());
108128
group.setDescription(groupEntity.getDescription());
109129

@@ -112,6 +132,17 @@ public Group createGroup(GroupEntity groupEntity) throws ServiceException {
112132

113133
group.getProperties().put("sharedRoster.displayName", groupEntity.getName());
114134
group.getProperties().put("sharedRoster.groupList", "");
135+
136+
final Collection<JID> members = group.getMembers();
137+
for (final JID newMember : newMembers) {
138+
// Unsure if #addAll works for the specific Collection subclass that is used in the Group implementation. Let's add them one-by-one to be safe.
139+
members.add(newMember);
140+
}
141+
final Collection<JID> admins = group.getAdmins();
142+
for (final JID newAdmin : newAdmins) {
143+
// Unsure if #addAll works for the specific Collection subclass that is used in the Group implementation. Let's add them one-by-one to be safe.
144+
admins.add(newAdmin);
145+
}
115146
} catch (GroupAlreadyExistsException e) {
116147
throw new ServiceException("Could not create a group", groupEntity.getName(),
117148
ExceptionType.GROUP_ALREADY_EXISTS, Response.Status.CONFLICT, e);
@@ -136,10 +167,65 @@ public Group updateGroup(String groupName, GroupEntity groupEntity) throws Servi
136167
if (groupEntity != null && !groupEntity.getName().isEmpty()) {
137168
if (groupName.equals(groupEntity.getName())) {
138169
try {
170+
final Collection<JID> newMembers = new HashSet<>();
171+
final Collection<JID> newAdmins = new HashSet<>();
172+
173+
// Input validation.
139174
group = GroupManager.getInstance().getGroup(groupName);
175+
for (final String newMember : groupEntity.getMembers()) {
176+
try {
177+
newMembers.add(newMember.contains("@") ? new JID(newMember) : XMPPServer.getInstance().createJID(newMember, null));
178+
} catch (IllegalArgumentException e) {
179+
throw new ServiceException("Cannot parse a member value as a JID: " + newMember, groupEntity.getName(), ExceptionType.ILLEGAL_ARGUMENT_EXCEPTION, Response.Status.BAD_REQUEST, e);
180+
}
181+
}
182+
for (final String newAdmin : groupEntity.getAdmins()) {
183+
try {
184+
newAdmins.add(newAdmin.contains("@") ? new JID(newAdmin) : XMPPServer.getInstance().createJID(newAdmin, null));
185+
} catch (IllegalArgumentException e) {
186+
throw new ServiceException("Cannot parse a admin value as a JID: " + newAdmin, groupEntity.getName(), ExceptionType.ILLEGAL_ARGUMENT_EXCEPTION, Response.Status.BAD_REQUEST, e);
187+
}
188+
}
189+
140190
group.setDescription(groupEntity.getDescription());
141191
final String showInRoster = groupEntity.getShared() ? "onlyGroup" : "nobody";
142192
group.getProperties().put("sharedRoster.showInRoster", showInRoster);
193+
194+
// Correct the member-list that already is in the group to match the desired state.
195+
final Collection<JID> members = group.getMembers();
196+
final Iterator<JID> membersIterator = members.iterator();
197+
while (membersIterator.hasNext()) {
198+
final JID oldMember = membersIterator.next();
199+
if (newMembers.contains(oldMember)) {
200+
// Already exists. No need to add it again.
201+
newMembers.remove(oldMember);
202+
} else {
203+
// No longer exists. Remove from group.
204+
membersIterator.remove();
205+
}
206+
}
207+
for (final JID newMember : newMembers) {
208+
// Unsure if #addAll works for the specific Collection subclass that is used in the Group implementation. Let's add them one-by-one to be safe.
209+
members.add(newMember);
210+
}
211+
212+
// Correct the admin-list that already is in the group to match the desired state.
213+
final Collection<JID> admins = group.getAdmins();
214+
final Iterator<JID> adminsIterator = admins.iterator();
215+
while (adminsIterator.hasNext()) {
216+
final JID oldAdmin = adminsIterator.next();
217+
if (newAdmins.contains(oldAdmin)) {
218+
// Already exists. No need to add it again.
219+
newAdmins.remove(oldAdmin);
220+
} else {
221+
// No longer exists. Remove from group.
222+
adminsIterator.remove();
223+
}
224+
}
225+
for (final JID newAdmin : newAdmins) {
226+
// Unsure if #addAll works for the specific Collection subclass that is used in the Group implementation. Let's add them one-by-one to be safe.
227+
admins.add(newAdmin);
228+
}
143229
} catch (GroupNotFoundException e) {
144230
throw new ServiceException("Could not find group", groupName, ExceptionType.GROUP_NOT_FOUND,
145231
Response.Status.NOT_FOUND, e);

src/java/org/jivesoftware/openfire/plugin/rest/service/GroupService.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ public GroupEntities getGroups() throws ServiceException
6161
description = "Create a new user group.",
6262
responses = {
6363
@ApiResponse(responseCode = "201", description = "Group created."),
64-
@ApiResponse(responseCode = "400", description = "Group or group name missing."),
64+
@ApiResponse(responseCode = "400", description = "Group or group name missing, or invalid syntax for a property."),
6565
@ApiResponse(responseCode = "409", description = "Group already exists.")
6666
})
6767
@Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })
@@ -94,7 +94,7 @@ public GroupEntity getGroup(@Parameter(description = "The name of the group that
9494
description = "Updates / overwrites an existing user group. Note that the name of the group cannot be changed.",
9595
responses = {
9696
@ApiResponse(responseCode = "200", description = "Group updated."),
97-
@ApiResponse(responseCode = "400", description = "Group or group name missing, or name does not match existing group."),
97+
@ApiResponse(responseCode = "400", description = "Group or group name missing, or name does not match existing group, or invalid syntax for a property."),
9898
@ApiResponse(responseCode = "404", description = "Group with this name not found."),
9999
})
100100
@Consumes({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON })

src/java/org/jivesoftware/openfire/plugin/rest/service/UserGroupService.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,10 @@ public UserGroupsEntity getUserGroups(
5959

6060
@POST
6161
@Operation( summary = "Add user to groups",
62-
description = "Add a particular user to a collection of groups.",
62+
description = "Add a particular user to a collection of groups. When a group that is provided does not exist, it will be automatically created if possible.",
6363
responses = {
6464
@ApiResponse(responseCode = "201", description = "The user was added to all groups."),
65+
@ApiResponse(responseCode = "400", description = "When the username cannot be parsed into a JID.")
6566
})
6667
@Consumes({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
6768
public Response addUserToGroups(
@@ -76,9 +77,10 @@ public Response addUserToGroups(
7677
@POST
7778
@Path("/{groupName}")
7879
@Operation( summary = "Add user to group",
79-
description = "Add a particular user to a particular group.",
80+
description = "Add a particular user to a particular group. When the group that does not exist, it will be automatically created if possible.",
8081
responses = {
8182
@ApiResponse(responseCode = "201", description = "The user was added to the groups."),
83+
@ApiResponse(responseCode = "400", description = "When the username cannot be parsed into a JID.")
8284
})
8385
public Response addUserToGroup(
8486
@Parameter(description = "The username of the user that is to be added to a group.", required = true) @PathParam("username") String username,

0 commit comments

Comments
 (0)