Commit d9ae3e1e authored by Matt Tucker's avatar Matt Tucker Committed by matt

Refactored PrivateStorage code.


git-svn-id: http://svn.igniterealtime.org/svn/repos/messenger/trunk@396 b35dd754-fafc-0310-a699-88a17e54d16e
parent 746feac7
...@@ -9,41 +9,28 @@ ...@@ -9,41 +9,28 @@
* a copy of which is included in this distribution. * a copy of which is included in this distribution.
*/ */
package org.jivesoftware.messenger.user.spi; package org.jivesoftware.messenger;
import org.jivesoftware.database.DbConnectionManager;
import org.jivesoftware.messenger.container.BasicModule; import org.jivesoftware.messenger.container.BasicModule;
import org.jivesoftware.messenger.container.Container; import org.jivesoftware.database.DbConnectionManager;
import org.jivesoftware.util.LocaleUtils;
import org.jivesoftware.util.Log; import org.jivesoftware.util.Log;
import org.jivesoftware.util.LocaleUtils;
import org.jivesoftware.util.XPPReader; import org.jivesoftware.util.XPPReader;
import org.jivesoftware.messenger.PrivateStore; import org.dom4j.Element;
import org.jivesoftware.messenger.JiveGlobals; import org.dom4j.Document;
import org.jivesoftware.messenger.auth.UnauthorizedException;
import java.io.StringReader; import java.sql.*;
import java.io.StringWriter;
import java.sql.Connection; import java.sql.Connection;
import java.sql.PreparedStatement; import java.io.StringWriter;
import java.sql.ResultSet; import java.io.StringReader;
import org.dom4j.Document;
import org.dom4j.Element;
/** /**
* <p>Store and retrieve data from the database.</p> * Private storage for user accounts (JEP-0049). It is used by some XMPP systems
* <p>The typical use cases don't give any advantage to caching the data so we won't. Direct * for saving client settings on the server.
* database access is used for all operations. We expect clients to retrieve private information
* on login, and set private information on logout, or when the user changes settings in the
* client application (occurs rarely and we expect the client to cache private data so we'll
* only see a get, then a set).</p>
* <p>We currently only match on namespace for backward compatibility,
* although it is possible that future versions may make the element name a distinct
* match requirement for requests. Therefore the element name as well as it's namespace
* is stored in the database even though it is currently not used for matching purposes.</p>
* *
* @author Iain Shigeoka * @author Iain Shigeoka
*/ */
public class DbPrivateStore extends BasicModule implements PrivateStore { public class PrivateStorage extends BasicModule {
private static final String LOAD_PRIVATE = private static final String LOAD_PRIVATE =
"SELECT value FROM jivePrivate WHERE username=? AND namespace=?"; "SELECT value FROM jivePrivate WHERE username=? AND namespace=?";
...@@ -52,30 +39,49 @@ public class DbPrivateStore extends BasicModule implements PrivateStore { ...@@ -52,30 +39,49 @@ public class DbPrivateStore extends BasicModule implements PrivateStore {
private static final String UPDATE_PRIVATE = private static final String UPDATE_PRIVATE =
"UPDATE jivePrivate SET value=?, name=? WHERE username=? AND namespace=?"; "UPDATE jivePrivate SET value=?, name=? WHERE username=? AND namespace=?";
// currently no delete supported, we can detect an add of an empty element and use that to // Currently no delete supported, we can detect an add of an empty element and
// signal a delete but that optimization doesn't seem necessary. // use that to signal a delete but that optimization doesn't seem necessary.
private static final String DELETE_PRIVATE = // private static final String DELETE_PRIVATE =
"DELETE FROM jivePrivate WHERE userID=? AND name=? AND namespace=?"; // "DELETE FROM jivePrivate WHERE userID=? AND name=? AND namespace=?";
// TODO: As with IQAuthHandler, this is not multi-server safe private boolean enabled = JiveGlobals.getBooleanProperty("xmpp.privateStorageEnabled", true);
private static boolean enabled;
public DbPrivateStore() { /**
* Constructs a new PrivateStore instance.
*/
public PrivateStorage() {
super("Private user data storage"); super("Private user data storage");
} }
/**
* Returns true if private storage is enabled.
*
* @return true if private storage is enabled.
*/
public boolean isEnabled() { public boolean isEnabled() {
return enabled; return enabled;
} }
/**
* Sets whether private storage is enabled.
*
* @param enabled true if this private store is enabled.
*/
public void setEnabled(boolean enabled) { public void setEnabled(boolean enabled) {
DbPrivateStore.enabled = enabled; this.enabled = enabled;
JiveGlobals.setProperty("xmpp.private", Boolean.toString(enabled)); JiveGlobals.setProperty("xmpp.privateStorageEnabled", Boolean.toString(enabled));
} }
public void add(String username, Element data) throws UnauthorizedException { /**
* Stores private data. If the name and namespace of the element matches another
* stored private data XML document, then replace it with the new one.
*
* @param data the data to store (XML element)
* @param username the username of the account where private data is being stored
*/
public void add(String username, Element data) {
if (enabled) { if (enabled) {
Connection con = null; java.sql.Connection con = null;
PreparedStatement pstmt = null; PreparedStatement pstmt = null;
try { try {
StringWriter writer = new StringWriter(); StringWriter writer = new StringWriter();
...@@ -116,7 +122,21 @@ public class DbPrivateStore extends BasicModule implements PrivateStore { ...@@ -116,7 +122,21 @@ public class DbPrivateStore extends BasicModule implements PrivateStore {
} }
} }
public Element get(String username, Element data) throws UnauthorizedException { /**
* Returns the data stored under a key corresponding to the name and namespace
* of the given element. The Element must be in the form:<p>
*
* <code>&lt;name xmlns='namespace'/&gt;</code><p>
*
* If no data is currently stored under the given key, an empty element will be
* returned.
*
* @param data an XML document who's element name and namespace is used to
* match previously stored private data.
* @param username the username of the account where private data is being stored.
* @return the data stored under the given key or the data element.
*/
public Element get(String username, Element data) {
data.clearContent(); data.clearContent();
if (enabled) { if (enabled) {
Connection con = null; Connection con = null;
...@@ -130,8 +150,9 @@ public class DbPrivateStore extends BasicModule implements PrivateStore { ...@@ -130,8 +150,9 @@ public class DbPrivateStore extends BasicModule implements PrivateStore {
if (rs.next()) { if (rs.next()) {
StringReader reader = new StringReader(rs.getString(1).trim()); StringReader reader = new StringReader(rs.getString(1).trim());
Document doc = XPPReader.parseDocument(reader, this.getClass()); Document doc = XPPReader.parseDocument(reader, this.getClass());
return doc.getRootElement(); data = doc.getRootElement();
} }
rs.close();
} }
catch (Exception e) { catch (Exception e) {
Log.error(LocaleUtils.getLocalizedString("admin.error"), e); Log.error(LocaleUtils.getLocalizedString("admin.error"), e);
...@@ -145,9 +166,4 @@ public class DbPrivateStore extends BasicModule implements PrivateStore { ...@@ -145,9 +166,4 @@ public class DbPrivateStore extends BasicModule implements PrivateStore {
} }
return data; return data;
} }
public void initialize(Container container) {
super.initialize(container);
enabled = JiveGlobals.getBooleanProperty("xmpp.private");
}
} }
\ No newline at end of file
/**
* $RCSfile$
* $Revision$
* $Date$
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.messenger;
import org.jivesoftware.messenger.auth.UnauthorizedException;
import org.dom4j.Element;
/**
* <p>Private storage for user accounts.</p>
* <p>Used by some XMPP systems for saving client settings on the server.</p>
*
* @author Iain Shigeoka
*/
public interface PrivateStore {
/**
* <p>Retrieve a flag indicating if the private store is enabled or not.</p>
* <p>Private storage is subject to resource abuse and must be managed. The first
* line of defense is simple to enable or disable private storage outright.</p>
*
* @return True if this private store is enabled (allowing at least some private storage)
*/
boolean isEnabled();
/**
* <p>Enable or disable the private store.</p>
* <p>Private storage is subject to resource abuse and must be managed. The first
* line of defense is simple to enable or disable private storage outright.</p>
*
* @param enabled True if this private store is enabled (allowing at least some private storage)
* @throws UnauthorizedException If there are insufficient permissions to access the data
*/
void setEnabled(boolean enabled) throws UnauthorizedException;
/**
* <p>Store the given data.</p>
* <p>If the name and namespace of the element matches another
* stored private data XML document, then replace it with the new one.</p>
*
* @param data the data to store (XML element)
* @param username the username of the account where private data is being stored
* @throws UnauthorizedException If there are insufficient permissions to access the data
*/
void add(String username, Element data) throws UnauthorizedException;
/**
* <p>Retrieve the data stored under a key corresponding to the name and namespace of
* the data element given.</p>
* <p>The data query will be of the form:</p>
* <code><pre>
* &lt;name xmlns='namespace'/&gt;
* </pre></code>
* <p>If no data is currently stored under the given key, return the query.</p>
*
* @param data An XML document who's element name and namespace is used to match previously stored private data
* @param username the username of the account where private data is being stored
* @return The data stored under the given key or the data element
* @throws UnauthorizedException If there are insufficient permissions to access the data
*/
Element get(String username, Element data) throws UnauthorizedException;
}
\ No newline at end of file
...@@ -64,11 +64,11 @@ public class IQPrivateHandler extends IQHandler implements ServerFeaturesProvide ...@@ -64,11 +64,11 @@ public class IQPrivateHandler extends IQHandler implements ServerFeaturesProvide
if (IQ.GET.equals(packet.getType())) { if (IQ.GET.equals(packet.getType())) {
replyPacket = packet.createResult(); replyPacket = packet.createResult();
PayloadFragment frag = new PayloadFragment("jabber:iq:private", "query"); PayloadFragment frag = new PayloadFragment("jabber:iq:private", "query");
frag.addFragment(new XMPPDOMFragment(privateStore.get(packet.getOriginatingSession().getUsername(), dataElement))); frag.addFragment(new XMPPDOMFragment(privateStorage.get(packet.getOriginatingSession().getUsername(), dataElement)));
replyPacket.setChildFragment(frag); replyPacket.setChildFragment(frag);
} }
else { else {
privateStore.add(packet.getOriginatingSession().getUsername(), dataElement); privateStorage.add(packet.getOriginatingSession().getUsername(), dataElement);
replyPacket = packet.createResult(); replyPacket = packet.createResult();
} }
} }
...@@ -84,11 +84,11 @@ public class IQPrivateHandler extends IQHandler implements ServerFeaturesProvide ...@@ -84,11 +84,11 @@ public class IQPrivateHandler extends IQHandler implements ServerFeaturesProvide
return replyPacket; return replyPacket;
} }
public PrivateStore privateStore = null; public PrivateStorage privateStorage = null;
protected TrackInfo getTrackInfo() { protected TrackInfo getTrackInfo() {
TrackInfo trackInfo = super.getTrackInfo(); TrackInfo trackInfo = super.getTrackInfo();
trackInfo.getTrackerClasses().put(PrivateStore.class, "privateStore"); trackInfo.getTrackerClasses().put(PrivateStorage.class, "privateStore");
return trackInfo; return trackInfo;
} }
......
/**
* $RCSfile$
* $Revision$
* $Date$
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.messenger.spi;
import org.jivesoftware.messenger.PrivateStore;
import org.jivesoftware.messenger.auth.AuthToken;
import org.jivesoftware.messenger.auth.Permissions;
import org.jivesoftware.messenger.auth.UnauthorizedException;
import org.dom4j.Element;
/**
* Standard security proxy.
*
* @author Iain Shigeoka
*/
public class PrivateStoreProxy implements PrivateStore {
/**
* The store being wrapped
*/
private PrivateStore store;
/**
* Authentication token
*/
private AuthToken authToken;
/**
* User permissions
*/
private Permissions permissions;
public PrivateStoreProxy(PrivateStore store, AuthToken authToken, Permissions permissions) {
this.store = store;
this.authToken = authToken;
this.permissions = permissions;
}
public boolean isEnabled() {
return store.isEnabled();
}
public void setEnabled(boolean enabled) throws UnauthorizedException {
if (permissions.hasPermission(Permissions.SYSTEM_ADMIN | Permissions.USER_ADMIN)) {
store.setEnabled(enabled);
}
else {
throw new UnauthorizedException();
}
}
public void add(String username, Element data) throws UnauthorizedException {
if (permissions.hasPermission(Permissions.SYSTEM_ADMIN | Permissions.USER_ADMIN)) {
store.add(username, data);
}
else {
throw new UnauthorizedException();
}
}
public Element get(String username, Element data) throws UnauthorizedException {
if (permissions.hasPermission(Permissions.SYSTEM_ADMIN | Permissions.USER_ADMIN)) {
return store.get(username, data);
}
else {
throw new UnauthorizedException();
}
}
}
/**
* $RCSfile$
* $Revision$
* $Date$
*
* Copyright (C) 2004 Jive Software. All rights reserved.
*
* This software is published under the terms of the GNU Public License (GPL),
* a copy of which is included in this distribution.
*/
package org.jivesoftware.messenger.user.spi;
import org.jivesoftware.messenger.PrivateStore;
import org.jivesoftware.util.Log;
import org.jivesoftware.util.LocaleUtils;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.Namespace;
import org.dom4j.QName;
final public class UserPrivateData {
private final Map settings = new HashMap();
private List elementList = new ArrayList();
private PrivateStore privateStore;
private String username;
final String settingsNamespace = "jive:user:settings";
final String settingsElementName = "personal_settings";
final Namespace NS = Namespace.get(settingsNamespace);
final QName namespace = DocumentHelper.createQName(settingsElementName, NS);
public UserPrivateData() {
}
public void setState(String username, PrivateStore privateStore) {
this.privateStore = privateStore;
this.username = username;
try {
final Element element = privateStore.get(username, DocumentHelper.createElement(namespace));
final List list = element.elements();
final Iterator iter = list.iterator();
while (iter.hasNext()) {
Element el = (Element)iter.next();
addToSettings(el);
}
}
catch (Exception ex) {
Log.error(LocaleUtils.getLocalizedString("admin.error"), ex);
}
}
private void addToSettings(Element elem) {
final Iterator iter = elem.elementIterator();
while (iter.hasNext()) {
final Element e = (Element)iter.next();
String name = e.getName();
String value = e.getText();
settings.put(name, value);
}
elementList.add(elem);
}
public String getProperty(String property) {
return (String)getSettings().get(property);
}
public void setProperty(String name, String value) {
getSettings().put(name, value);
}
public Map getSettings() {
return settings;
}
public void save() {
setMap(getSettings());
}
public void setMap(Map map) {
Element element = DocumentHelper.createElement(namespace);
final Iterator i = element.elementIterator();
while (i.hasNext()) {
element.remove((Element)i.next());
}
final Iterator iter = map.keySet().iterator();
while (iter.hasNext()) {
String key = (String)iter.next();
String value = (String)getSettings().get(key);
Element elem = DocumentHelper.createElement("entry");
elem.addElement(key).setText(value);
element.add(elem);
}
try {
privateStore.add(username, element);
}
catch (Exception ex) {
Log.error(LocaleUtils.getLocalizedString("admin.error"), ex);
}
}
}
\ No newline at end of file
...@@ -21,7 +21,7 @@ import org.jivesoftware.messenger.user.User; ...@@ -21,7 +21,7 @@ import org.jivesoftware.messenger.user.User;
import org.jivesoftware.messenger.user.UserManager; import org.jivesoftware.messenger.user.UserManager;
import org.jivesoftware.messenger.user.RosterManager; import org.jivesoftware.messenger.user.RosterManager;
import org.jivesoftware.messenger.XMPPServer; import org.jivesoftware.messenger.XMPPServer;
import org.jivesoftware.messenger.PrivateStore; import org.jivesoftware.messenger.PrivateStorage;
import org.jivesoftware.messenger.PresenceManager; import org.jivesoftware.messenger.PresenceManager;
import org.jivesoftware.messenger.SessionManager; import org.jivesoftware.messenger.SessionManager;
import org.jivesoftware.messenger.XMPPServerInfo; import org.jivesoftware.messenger.XMPPServerInfo;
...@@ -98,8 +98,8 @@ public class WebManager extends WebBean { ...@@ -98,8 +98,8 @@ public class WebManager extends WebBean {
return (RosterManager)getServiceLookup().lookup(RosterManager.class); return (RosterManager)getServiceLookup().lookup(RosterManager.class);
} }
public PrivateStore getPrivateStore() { public PrivateStorage getPrivateStore() {
return (PrivateStore)getServiceLookup().lookup(PrivateStore.class); return (PrivateStorage)getServiceLookup().lookup(PrivateStorage.class);
} }
public PresenceManager getPresenceManager() { public PresenceManager getPresenceManager() {
......
...@@ -37,11 +37,11 @@ ...@@ -37,11 +37,11 @@
boolean privateEnabled = ParamUtils.getBooleanParameter(request,"privateEnabled"); boolean privateEnabled = ParamUtils.getBooleanParameter(request,"privateEnabled");
// Get an audit manager: // Get an audit manager:
PrivateStore privateStore = admin.getPrivateStore(); PrivateStorage privateStorage = admin.getPrivateStore();
Map errors = new HashMap(); Map errors = new HashMap();
if( update ) { if (update) {
privateStore.setEnabled(privateEnabled); privateStorage.setEnabled(privateEnabled);
%> %>
<div class="jive-success"> <div class="jive-success">
<table cellpadding="0" cellspacing="0" border="0"> <table cellpadding="0" cellspacing="0" border="0">
...@@ -59,7 +59,7 @@ ...@@ -59,7 +59,7 @@
// Set page vars // Set page vars
if (errors.size() == 0) { if (errors.size() == 0) {
privateEnabled = privateStore.isEnabled(); privateEnabled = privateStorage.isEnabled();
} }
%> %>
......
...@@ -32,7 +32,6 @@ ...@@ -32,7 +32,6 @@
<jsp:useBean id="webManager" class="org.jivesoftware.util.WebManager" /> <jsp:useBean id="webManager" class="org.jivesoftware.util.WebManager" />
<jsp:useBean id="errors" class="java.util.HashMap" /> <jsp:useBean id="errors" class="java.util.HashMap" />
<jsp:useBean id="userData" class="org.jivesoftware.messenger.user.spi.UserPrivateData" />
<% webManager.init(request, response, session, application, out ); %> <% webManager.init(request, response, session, application, out ); %>
<% // Get parameters // <% // Get parameters //
......
...@@ -22,7 +22,6 @@ ...@@ -22,7 +22,6 @@
<%@ taglib uri="core" prefix="c"%> <%@ taglib uri="core" prefix="c"%>
<jsp:useBean id="webManager" class="org.jivesoftware.util.WebManager" /> <jsp:useBean id="webManager" class="org.jivesoftware.util.WebManager" />
<jsp:useBean id="userData" class="org.jivesoftware.messenger.user.spi.UserPrivateData" />
<% // Get parameters <% // Get parameters
boolean save = ParamUtils.getBooleanParameter(request,"save"); boolean save = ParamUtils.getBooleanParameter(request,"save");
...@@ -40,10 +39,6 @@ ...@@ -40,10 +39,6 @@
// Load the user object // Load the user object
User user = webManager.getUserManager().getUser(username); User user = webManager.getUserManager().getUser(username);
// Get a private data manager //
final PrivateStore privateStore = webManager.getPrivateStore();
userData.setState( user.getUsername(), privateStore );
// Handle a save // Handle a save
Map errors = new HashMap(); Map errors = new HashMap();
if (save) { if (save) {
......
...@@ -21,7 +21,6 @@ ...@@ -21,7 +21,6 @@
<%@ taglib uri="core" prefix="c"%> <%@ taglib uri="core" prefix="c"%>
<jsp:useBean id="webManager" class="org.jivesoftware.util.WebManager" /> <jsp:useBean id="webManager" class="org.jivesoftware.util.WebManager" />
<jsp:useBean id="userData" class="org.jivesoftware.messenger.user.spi.UserPrivateData" />
<% // Get parameters // <% // Get parameters //
boolean cancel = request.getParameter("cancel") != null; boolean cancel = request.getParameter("cancel") != null;
...@@ -69,16 +68,6 @@ ...@@ -69,16 +68,6 @@
DateFormat formatter = DateFormat.getDateInstance(DateFormat.MEDIUM); DateFormat formatter = DateFormat.getDateInstance(DateFormat.MEDIUM);
%> %>
<%
// Get a private data manager //
final PrivateStore privateStore = webManager.getPrivateStore();
userData.setState( user.getUsername(), privateStore );
String nickname = userData.getProperty( "nickname" );
if(nickname == null){
nickname = "";
}
%>
<jsp:useBean id="pageinfo" scope="request" class="org.jivesoftware.admin.AdminPageBean" /> <jsp:useBean id="pageinfo" scope="request" class="org.jivesoftware.admin.AdminPageBean" />
<% // Title of this page and breadcrumbs <% // Title of this page and breadcrumbs
String title = "User Properties"; String title = "User Properties";
......
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