Commit 2d4a1082 authored by Tom Evans's avatar Tom Evans Committed by tevans

OF-674: Add roster management capabilities for userservice plugin

git-svn-id: http://svn.igniterealtime.org/svn/repos/openfire/trunk@13665 b35dd754-fafc-0310-a699-88a17e54d16e
parent fcd938c5
...@@ -44,6 +44,12 @@ ...@@ -44,6 +44,12 @@
User Service Plugin Changelog User Service Plugin Changelog
</h1> </h1>
<p><b>1.3.3</b> -- May 22, 2013</p>
<ul>
<li>Merged proposed changes from community to add roster management capabilities (<a href="http://issues.igniterealtime.org/browse/OF-674">OF-674</a>).</li>
</ul>
<p><b>1.3.2</b> -- August 19, 2009</p> <p><b>1.3.2</b> -- August 19, 2009</p>
<ul> <ul>
<li>String sanitize username, so that escaped JIDs work. Author: Daryl Herzmann</li> <li>String sanitize username, so that escaped JIDs work. Author: Daryl Herzmann</li>
......
...@@ -5,8 +5,8 @@ ...@@ -5,8 +5,8 @@
<name>User Service</name> <name>User Service</name>
<description>Allows administration of users via HTTP requests.</description> <description>Allows administration of users via HTTP requests.</description>
<author>Justin Hunt</author> <author>Justin Hunt</author>
<version>1.3.2</version> <version>1.3.3</version>
<date>08/19/2009</date> <date>05/22/2013</date>
<minServerVersion>3.5.1</minServerVersion> <minServerVersion>3.5.1</minServerVersion>
<adminconsole> <adminconsole>
......
...@@ -57,7 +57,7 @@ User Service Plugin Readme ...@@ -57,7 +57,7 @@ User Service Plugin Readme
<h2>Overview</h2> <h2>Overview</h2>
<p> <p>
The User Service Plugin provides the ability to add,edit,delete users by sending an http request to the server. The User Service Plugin provides the ability to add,edit,delete users and manage their rosters by sending an http request to the server.
It is intended to be used by applications automating the user administration process. It is intended to be used by applications automating the user administration process.
This plugin's functionality is useful for applications that need to administer users outside of the Openfire admin console. This plugin's functionality is useful for applications that need to administer users outside of the Openfire admin console.
An example of such an application might be a live sports reporting application that uses XMPP as its transport, and An example of such an application might be a live sports reporting application that uses XMPP as its transport, and
...@@ -89,13 +89,13 @@ if your server name is "example.com", the URL is http://example.com/plugins/user ...@@ -89,13 +89,13 @@ if your server name is "example.com", the URL is http://example.com/plugins/user
The following parameters can be passed into the request:<p> The following parameters can be passed into the request:<p>
<div id="datatable"> <div id="datatable">
<table cellpadding="3" cellspacing="1" border="0" width="700"> <table cellpadding="3" cellspacing="1" border="0" width="900">
<tr> <tr>
<th colspan=2>Name</th><th>Description</th> <th colspan=2>Name</th><th>Description</th>
</tr> </tr>
<tr> <tr>
<td class="name">type</td><td>Required</td><td>The admin service required. <td class="name">type</td><td>Required</td><td>The admin service required.
Possible values are add, delete, update, enable, disable</td> Possible values are 'add', 'delete', 'update', 'enable', 'disable', 'add_roster', 'update_roster', 'delete_roster'.</td>
</tr> </tr>
<tr> <tr>
<td class="name">secret</td><td>Required</td> <td class="name">secret</td><td>Required</td>
...@@ -103,15 +103,15 @@ The following parameters can be passed into the request:<p> ...@@ -103,15 +103,15 @@ The following parameters can be passed into the request:<p>
</tr> </tr>
<tr> <tr>
<td class="name">username</td><td>Required</td> <td class="name">username</td><td>Required</td>
<td>The username of the user to add, update, delete, enable, or disable. ie the part before the @ symbol.</td> <td>The username of the user to 'add', 'delete', 'update', 'enable', 'disable', 'add_roster', 'update_roster', 'delete_roster'. ie the part before the @ symbol.</td>
</tr> </tr>
<tr> <tr>
<td class="name">password</td><td>Required for add operation</td> <td class="name">password</td><td>Required for 'add' operation</td>
<td>The password of the new user or the user being updated.</td> <td>The password of the new user or the user being updated.</td>
</tr> </tr>
<tr> <tr>
<td class="name">name</td><td>Optional</td> <td class="name">name</td><td>Optional</td>
<td>The display name of the new user or the user being updated.</td> <td>The display name of the new user or the user being updated. For 'add_roster', 'update_roster' operations specifies the nickname of the roster item.</td>
</tr> </tr>
<tr> <tr>
<td class="name">email</td><td>Optional</td> <td class="name">email</td><td>Optional</td>
...@@ -121,6 +121,14 @@ The following parameters can be passed into the request:<p> ...@@ -121,6 +121,14 @@ The following parameters can be passed into the request:<p>
<td class="name">groups</td><td>Optional</td> <td class="name">groups</td><td>Optional</td>
<td>List of groups where the user is a member. Values are comma delimited.</td> <td>List of groups where the user is a member. Values are comma delimited.</td>
</tr> </tr>
<tr>
<td class="name">item_jid</td><td>Required for 'add_roster', 'update_roster', 'delete_roster' operations.</td>
<td>The JID of the roster item</td>
</tr>
<tr>
<td class="name">subscription</td><td>Optional</td>
<td>Type of subscription for 'add_roster', 'update_roster' operations. Possible numeric values are: -1(remove), 0(none), 1(to), 2(from), 3(both).</td>
</tr>
</table><p> </table><p>
...@@ -169,7 +177,7 @@ http://example.com:9090/plugins/userService/userservice?type=enable&secret=bigse ...@@ -169,7 +177,7 @@ http://example.com:9090/plugins/userService/userservice?type=enable&secret=bigse
</ul> </ul>
This example updates a user The following example updates a user
<ul> <ul>
<form> <form>
...@@ -179,6 +187,36 @@ http://example.com:9090/plugins/userService/userservice?type=update&secret=bigse ...@@ -179,6 +187,36 @@ http://example.com:9090/plugins/userService/userservice?type=update&secret=bigse
</form> </form>
</ul> </ul>
The following example adds new roster item with subscription 'both' for user 'kafka'
<ul>
<form>
<textarea cols=65 rows=3 wrap=virtual>
http://example.com:9090/plugins/userService/userservice?type=add_roster&secret=bigsecret&username=kafka&item_jid=franz@example.com&name=franz&subscription=3
</textarea>
</form>
</ul>
The following example updates existing roster item to subscription 'none' for user 'kafka'
<ul>
<form>
<textarea cols=65 rows=3 wrap=virtual>
http://example.com:9090/plugins/userService/userservice?type=update_roster&secret=bigsecret&username=kafka&item_jid=franz@example.com&name=franz&subscription=0
</textarea>
</form>
</ul>
The following example deletes roster item 'franz@kafka.com' for user 'kafka'
<ul>
<form>
<textarea cols=65 rows=3 wrap=virtual>
http://example.com:9090/plugins/userService/userservice?type=delete_roster&secret=bigsecret&username=kafka&item_jid=franz@example.com
</textarea>
</form>
</ul>
<br><br> <br><br>
* When sending double characters (Chinese/Japanese/Korean etc) you should URLEncode the string as utf8.<br> * When sending double characters (Chinese/Japanese/Korean etc) you should URLEncode the string as utf8.<br>
In Java this is done like this<br> In Java this is done like this<br>
...@@ -195,7 +233,7 @@ If the request was unsuccessful, the return will be an "error" element with a te ...@@ -195,7 +233,7 @@ If the request was unsuccessful, the return will be an "error" element with a te
<div id="datatable"> <div id="datatable">
<table cellpadding="3" cellspacing="1" border="0" width="700"> <table cellpadding="3" cellspacing="1" border="0" width="900">
<tr> <tr>
<th >Error String</th><th>Description</th> <th >Error String</th><th>Description</th>
</tr> </tr>
...@@ -205,12 +243,11 @@ If the request was unsuccessful, the return will be an "error" element with a te ...@@ -205,12 +243,11 @@ If the request was unsuccessful, the return will be an "error" element with a te
</tr> </tr>
<tr> <tr>
<td class="name">UserNotFoundException</td> <td class="name">UserNotFoundException</td>
<td>No user of the name specified, for a delete or update operation, exists on this server.</td> <td>No user of the name specified, for a delete or update operation, exists on this server. For 'update_roster' operation, roster item to be updated was not found.</td>
</tr> </tr>
<tr> <tr>
<td class="name">UserAlreadyExistsException</td> <td class="name">UserAlreadyExistsException</td>
<td>A user with the same name as the user about to be added, already exists. <td>A user with the same name as the user about to be added, already exists. For 'add_roster' operation, roster item with the same JID already exists.</td>
</td>
</tr> </tr>
<tr> <tr>
<td class="name">RequestNotAuthorised</td> <td class="name">RequestNotAuthorised</td>
...@@ -220,7 +257,10 @@ If the request was unsuccessful, the return will be an "error" element with a te ...@@ -220,7 +257,10 @@ If the request was unsuccessful, the return will be an "error" element with a te
<td class="name">UserServiceDisabled</td> <td class="name">UserServiceDisabled</td>
<td>The User Service is currently set to disabled in the Admin Console.</td> <td>The User Service is currently set to disabled in the Admin Console.</td>
</tr> </tr>
<tr>
<td class="name">SharedGroupException</td>
<td>Roster item can not be added/deleted to/from a shared group for operations with roster.</td>
</tr>
</table><p> </table><p>
......
...@@ -19,6 +19,15 @@ ...@@ -19,6 +19,15 @@
package org.jivesoftware.openfire.plugin; package org.jivesoftware.openfire.plugin;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;
import org.jivesoftware.openfire.SharedGroupException;
import org.jivesoftware.openfire.XMPPServer; import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.container.Plugin; import org.jivesoftware.openfire.container.Plugin;
import org.jivesoftware.openfire.container.PluginManager; import org.jivesoftware.openfire.container.PluginManager;
...@@ -26,19 +35,19 @@ import org.jivesoftware.openfire.group.Group; ...@@ -26,19 +35,19 @@ import org.jivesoftware.openfire.group.Group;
import org.jivesoftware.openfire.group.GroupManager; import org.jivesoftware.openfire.group.GroupManager;
import org.jivesoftware.openfire.group.GroupNotFoundException; import org.jivesoftware.openfire.group.GroupNotFoundException;
import org.jivesoftware.openfire.lockout.LockOutManager; import org.jivesoftware.openfire.lockout.LockOutManager;
import org.jivesoftware.openfire.roster.Roster;
import org.jivesoftware.openfire.roster.RosterItem;
import org.jivesoftware.openfire.roster.RosterManager;
import org.jivesoftware.openfire.user.User; import org.jivesoftware.openfire.user.User;
import org.jivesoftware.openfire.user.UserAlreadyExistsException; import org.jivesoftware.openfire.user.UserAlreadyExistsException;
import org.jivesoftware.openfire.user.UserManager; import org.jivesoftware.openfire.user.UserManager;
import org.jivesoftware.openfire.user.UserNotFoundException; import org.jivesoftware.openfire.user.UserNotFoundException;
import org.jivesoftware.util.JiveGlobals; import org.jivesoftware.util.JiveGlobals;
import org.jivesoftware.util.StringUtils;
import org.jivesoftware.util.PropertyEventListener;
import org.jivesoftware.util.PropertyEventDispatcher; import org.jivesoftware.util.PropertyEventDispatcher;
import org.jivesoftware.util.PropertyEventListener;
import org.jivesoftware.util.StringUtils;
import org.xmpp.packet.JID; import org.xmpp.packet.JID;
import java.io.File;
import java.util.*;
/** /**
* Plugin that allows the administration of users via HTTP requests. * Plugin that allows the administration of users via HTTP requests.
* *
...@@ -46,6 +55,7 @@ import java.util.*; ...@@ -46,6 +55,7 @@ import java.util.*;
*/ */
public class UserServicePlugin implements Plugin, PropertyEventListener { public class UserServicePlugin implements Plugin, PropertyEventListener {
private UserManager userManager; private UserManager userManager;
private RosterManager rosterManager;
private XMPPServer server; private XMPPServer server;
private String secret; private String secret;
...@@ -55,7 +65,8 @@ public class UserServicePlugin implements Plugin, PropertyEventListener { ...@@ -55,7 +65,8 @@ public class UserServicePlugin implements Plugin, PropertyEventListener {
public void initializePlugin(PluginManager manager, File pluginDirectory) { public void initializePlugin(PluginManager manager, File pluginDirectory) {
server = XMPPServer.getInstance(); server = XMPPServer.getInstance();
userManager = server.getUserManager(); userManager = server.getUserManager();
rosterManager = server.getRosterManager();
secret = JiveGlobals.getProperty("plugin.userservice.secret", ""); secret = JiveGlobals.getProperty("plugin.userservice.secret", "");
// If no secret key has been assigned to the user service yet, assign a random one. // If no secret key has been assigned to the user service yet, assign a random one.
if (secret.equals("")){ if (secret.equals("")){
...@@ -168,7 +179,110 @@ public class UserServicePlugin implements Plugin, PropertyEventListener { ...@@ -168,7 +179,110 @@ public class UserServicePlugin implements Plugin, PropertyEventListener {
} }
} }
} }
/**
* Add new roster item for specified user
*
* @param username the username of the local user to add roster item to.
* @param itemJID the JID of the roster item to be added.
* @param itemName the nickname of the roster item.
* @param subscription the type of subscription of the roster item. Possible values are: -1(remove), 0(none), 1(to), 2(from), 3(both).
* @param groupNames the name of a group to place contact into.
* @throws UserNotFoundException if the user does not exist in the local server.
* @throws UserAlreadyExistsException if roster item with the same JID already exists.
* @throws SharedGroupException if roster item cannot be added to a shared group.
*/
public void addRosterItem(String username, String itemJID, String itemName, String subscription, String groupNames)
throws UserNotFoundException, UserAlreadyExistsException, SharedGroupException
{
getUser(username);
Roster r = rosterManager.getRoster(username);
JID j = new JID(itemJID);
try {
r.getRosterItem(j);
throw new UserAlreadyExistsException(j.toBareJID());
}
catch (UserNotFoundException e) {
//Roster item does not exist. Try to add it.
}
if (r != null) {
List<String> groups = new ArrayList<String>();
if (groupNames != null) {
StringTokenizer tkn = new StringTokenizer(groupNames, ",");
while (tkn.hasMoreTokens()) {
groups.add(tkn.nextToken());
}
}
RosterItem ri = r.createRosterItem(j, itemName, groups, false, true);
if (subscription == null) {
subscription = "0";
}
ri.setSubStatus(RosterItem.SubType.getTypeFromInt(Integer.parseInt(subscription)));
r.updateRosterItem(ri);
}
}
/**
* Update roster item for specified user
*
* @param username the username of the local user to update roster item for.
* @param itemJID the JID of the roster item to be updated.
* @param itemName the nickname of the roster item.
* @param subscription the type of subscription of the roster item. Possible values are: -1(remove), 0(none), 1(to), 2(from), 3(both).
* @param groupNames the name of a group.
* @throws UserNotFoundException if the user does not exist in the local server or roster item does not exist.
* @throws SharedGroupException if roster item cannot be added to a shared group.
*/
public void updateRosterItem(String username, String itemJID, String itemName, String subscription, String groupNames)
throws UserNotFoundException, SharedGroupException
{
getUser(username);
Roster r = rosterManager.getRoster(username);
JID j = new JID(itemJID);
RosterItem ri = r.getRosterItem(j);
List<String> groups = new ArrayList<String>();
if (groupNames != null) {
StringTokenizer tkn = new StringTokenizer(groupNames, ",");
while (tkn.hasMoreTokens()) {
groups.add(tkn.nextToken());
}
}
ri.setGroups(groups);
ri.setNickname(itemName);
if (subscription == null) {
subscription = "0";
}
ri.setSubStatus(RosterItem.SubType.getTypeFromInt(Integer.parseInt(subscription)));
r.updateRosterItem(ri);
}
/**
* Delete roster item for specified user. No error returns if nothing to delete.
*
* @param username the username of the local user to add roster item to.
* @param itemJID the JID of the roster item to be deleted.
* @throws UserNotFoundException if the user does not exist in the local server.
* @throws SharedGroupException if roster item cannot be deleted from a shared group.
*/
public void deleteRosterItem(String username, String itemJID)
throws UserNotFoundException, SharedGroupException
{
getUser(username);
Roster r = rosterManager.getRoster(username);
JID j = new JID(itemJID);
// No roster item is found. Uncomment the following line to throw UserNotFoundException.
//r.getRosterItem(j);
r.deleteRosterItem(j, true);
}
/** /**
* Returns the the requested user or <tt>null</tt> if there are any * Returns the the requested user or <tt>null</tt> if there are any
* problems that don't throw an error. * problems that don't throw an error.
......
...@@ -32,6 +32,7 @@ import javax.servlet.http.HttpServletRequest; ...@@ -32,6 +32,7 @@ import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import org.jivesoftware.admin.AuthCheckFilter; import org.jivesoftware.admin.AuthCheckFilter;
import org.jivesoftware.openfire.SharedGroupException;
import org.jivesoftware.openfire.XMPPServer; import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.plugin.UserServicePlugin; import org.jivesoftware.openfire.plugin.UserServicePlugin;
import org.jivesoftware.openfire.user.UserAlreadyExistsException; import org.jivesoftware.openfire.user.UserAlreadyExistsException;
...@@ -99,6 +100,8 @@ public class UserServiceServlet extends HttpServlet { ...@@ -99,6 +100,8 @@ public class UserServiceServlet extends HttpServlet {
String type = request.getParameter("type"); String type = request.getParameter("type");
String secret = request.getParameter("secret"); String secret = request.getParameter("secret");
String groupNames = request.getParameter("groups"); String groupNames = request.getParameter("groups");
String item_jid = request.getParameter("item_jid");
String sub = request.getParameter("subscription");
//No defaults, add, delete, update only //No defaults, add, delete, update only
//type = type == null ? "image" : type; //type = type == null ? "image" : type;
...@@ -122,6 +125,12 @@ public class UserServiceServlet extends HttpServlet { ...@@ -122,6 +125,12 @@ public class UserServiceServlet extends HttpServlet {
return; return;
} }
if ((type.equals("add_roster") || type.equals("update_roster") || type.equals("delete_roster")) &&
(item_jid == null || !(sub == null || sub.equals("-1") || sub.equals("0") ||
sub.equals("1") || sub.equals("2") || sub.equals("3")))) {
replyError("IllegalArgumentException",response, out);
return;
}
// Check the request type and process accordingly // Check the request type and process accordingly
try { try {
...@@ -151,6 +160,18 @@ public class UserServiceServlet extends HttpServlet { ...@@ -151,6 +160,18 @@ public class UserServiceServlet extends HttpServlet {
replyMessage("ok",response,out); replyMessage("ok",response,out);
//xmlProvider.sendInfo(request, response, presence); //xmlProvider.sendInfo(request, response, presence);
} }
else if ("add_roster".equals(type)) {
plugin.addRosterItem(username, item_jid, name, sub, groupNames);
replyMessage("ok",response, out);
}
else if ("update_roster".equals(type)) {
plugin.updateRosterItem(username, item_jid, name, sub, groupNames);
replyMessage("ok",response, out);
}
else if ("delete_roster".equals(type)) {
plugin.deleteRosterItem(username, item_jid);
replyMessage("ok",response, out);
}
else { else {
Log.warn("The userService servlet received an invalid request of type: " + type); Log.warn("The userService servlet received an invalid request of type: " + type);
// TODO Do something // TODO Do something
...@@ -166,6 +187,10 @@ public class UserServiceServlet extends HttpServlet { ...@@ -166,6 +187,10 @@ public class UserServiceServlet extends HttpServlet {
replyError("IllegalArgumentException",response, out); replyError("IllegalArgumentException",response, out);
} }
catch (SharedGroupException e) {
replyError("SharedGroupException",response, out);
}
catch (Exception e) { catch (Exception e) {
replyError(e.toString(),response, out); replyError(e.toString(),response, out);
} }
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment