Commit eacb38f9 authored by nobelium's avatar nobelium

OF-858: Auto create new node in PEP

OF-858: Disco will give pubsub#auto-create for PEP

OF-858: Adding Pubsub createNode Helper
parent 8e9fe62b
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
package org.jivesoftware.openfire.pep; package org.jivesoftware.openfire.pep;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator; import java.util.Iterator;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
...@@ -278,7 +279,14 @@ public class IQPEPHandler extends IQHandler implements ServerIdentitiesProvider, ...@@ -278,7 +279,14 @@ public class IQPEPHandler extends IQHandler implements ServerIdentitiesProvider,
* in the server's disco#info result (as per section 4 of XEP-0163). * in the server's disco#info result (as per section 4 of XEP-0163).
*/ */
public Iterator<String> getFeatures() { public Iterator<String> getFeatures() {
return XMPPServer.getInstance().getPubSubModule().getFeatures(null, null, null); Iterator<String> it = XMPPServer.getInstance().getPubSubModule().getFeatures(null, null, null);
ArrayList<String> features = new ArrayList<String>();
while (it.hasNext()) {
features.add(it.next());
}
// Auto Creation of nodes is supported in PEP
features.add("http://jabber.org/protocol/pubsub#auto-create");
return features.iterator();
} }
......
...@@ -39,6 +39,7 @@ import org.jivesoftware.openfire.RoutingTable; ...@@ -39,6 +39,7 @@ import org.jivesoftware.openfire.RoutingTable;
import org.jivesoftware.openfire.XMPPServer; import org.jivesoftware.openfire.XMPPServer;
import org.jivesoftware.openfire.XMPPServerListener; import org.jivesoftware.openfire.XMPPServerListener;
import org.jivesoftware.openfire.component.InternalComponentManager; import org.jivesoftware.openfire.component.InternalComponentManager;
import org.jivesoftware.openfire.pep.PEPService;
import org.jivesoftware.openfire.pubsub.cluster.RefreshNodeTask; import org.jivesoftware.openfire.pubsub.cluster.RefreshNodeTask;
import org.jivesoftware.openfire.pubsub.models.AccessModel; import org.jivesoftware.openfire.pubsub.models.AccessModel;
import org.jivesoftware.openfire.user.UserManager; import org.jivesoftware.openfire.user.UserManager;
...@@ -52,6 +53,7 @@ import org.xmpp.packet.IQ; ...@@ -52,6 +53,7 @@ import org.xmpp.packet.IQ;
import org.xmpp.packet.JID; import org.xmpp.packet.JID;
import org.xmpp.packet.Message; import org.xmpp.packet.Message;
import org.xmpp.packet.PacketError; import org.xmpp.packet.PacketError;
import org.xmpp.packet.PacketError.Condition;
import org.xmpp.packet.Presence; import org.xmpp.packet.Presence;
/** /**
...@@ -315,8 +317,14 @@ public class PubSubEngine { ...@@ -315,8 +317,14 @@ public class PubSubEngine {
private void publishItemsToNode(PubSubService service, IQ iq, Element publishElement) { private void publishItemsToNode(PubSubService service, IQ iq, Element publishElement) {
String nodeID = publishElement.attributeValue("node"); String nodeID = publishElement.attributeValue("node");
Node node; Node node;
JID from = iq.getFrom();
// TODO Assuming that owner is the bare JID (as defined in the JEP). This can be replaced with an explicit owner specified in the packet
JID owner = from.asBareJID();
if (nodeID == null) { if (nodeID == null) {
// No node was specified. Return bad_request error // XEP-0060 Section 7.2.3.3 - No node was specified. Return bad_request error
// This suggests that Instant nodes should not be auto-created
Element pubsubError = DocumentHelper.createElement(QName.get( Element pubsubError = DocumentHelper.createElement(QName.get(
"nodeid-required", "http://jabber.org/protocol/pubsub#errors")); "nodeid-required", "http://jabber.org/protocol/pubsub#errors"));
sendErrorPacket(iq, PacketError.Condition.bad_request, pubsubError); sendErrorPacket(iq, PacketError.Condition.bad_request, pubsubError);
...@@ -326,15 +334,29 @@ public class PubSubEngine { ...@@ -326,15 +334,29 @@ public class PubSubEngine {
// Look for the specified node // Look for the specified node
node = service.getNode(nodeID); node = service.getNode(nodeID);
if (node == null) { if (node == null) {
// Node does not exist. Return item-not-found error if (service instanceof PEPService && service.isServiceAdmin(owner)){
sendErrorPacket(iq, PacketError.Condition.item_not_found, null); // If it is a PEP service & publisher is service owner -
return; // auto create nodes.
Element childElement = iq.getChildElement();
Element createElement = publishElement.element("publish");
CreateNodeResponse response = createNodeHelper(service, iq, childElement, createElement);
if (response.newNode == null) {
// New node creation failed. Since pep#auto-create is advertised
// in disco#info, node creation error should be sent to the client.
sendErrorPacket(iq, response.creationStatus, response.pubsubError);
} else {
// Node creation succeeded, set node to newNode.
node = response.newNode;
}
} else {
// Node does not exist. Return item-not-found error
sendErrorPacket(iq, PacketError.Condition.item_not_found, null);
return;
}
} }
} }
JID from = iq.getFrom();
// TODO Assuming that owner is the bare JID (as defined in the JEP). This can be replaced with an explicit owner specified in the packet
JID owner = from.asBareJID();
if (!node.getPublisherModel().canPublish(node, owner) && !service.isServiceAdmin(owner)) { if (!node.getPublisherModel().canPublish(node, owner) && !service.isServiceAdmin(owner)) {
// Entity does not have sufficient privileges to publish to node // Entity does not have sufficient privileges to publish to node
sendErrorPacket(iq, PacketError.Condition.forbidden, null); sendErrorPacket(iq, PacketError.Condition.forbidden, null);
...@@ -1084,13 +1106,61 @@ public class PubSubEngine { ...@@ -1084,13 +1106,61 @@ public class PubSubEngine {
} }
private void createNode(PubSubService service, IQ iq, Element childElement, Element createElement) { private void createNode(PubSubService service, IQ iq, Element childElement, Element createElement) {
// Call createNodeHelper and get the node creation status.
CreateNodeResponse response = createNodeHelper(service, iq, childElement, createElement);
if (response.newNode == null) {
// New node creation failed
sendErrorPacket(iq, response.creationStatus, response.pubsubError);
} else {
IQ reply = IQ.createResultIQ(iq);
Node newNode = response.newNode;
String nodeID = createElement.attributeValue("node");
// Include new nodeID if it has changed from the original nodeID
if (!newNode.getNodeID().equals(nodeID)) {
Element elem = reply.setChildElement("pubsub", "http://jabber.org/protocol/pubsub");
elem.addElement("create").addAttribute("node", newNode.getNodeID());
}
router.route(reply);
}
}
/**
* Response Object returned by createNodeHelper method
*/
private class CreateNodeResponse {
public final PacketError.Condition creationStatus;
public final Node newNode;
public final Element pubsubError;
public CreateNodeResponse(PacketError.Condition creationStatus, Element pubsubError, Node newNode) {
this.creationStatus = creationStatus;
this.newNode = newNode;
this.pubsubError = pubsubError;
}
}
/**
* Checks if the following conditions are satisfied and creates a node
* - Requester can create nodes
* - Instant node creation is enabled
* - Node does not already exist
* - New node configuration is valid
*
* NOTE: This method should not reply to the client
*
* @param service
* @param iq
* @param childElement
* @param createElement
* @return
*/
private CreateNodeResponse createNodeHelper(PubSubService service, IQ iq, Element childElement, Element createElement) {
// Get sender of the IQ packet // Get sender of the IQ packet
JID from = iq.getFrom(); JID from = iq.getFrom();
// Verify that sender has permissions to create nodes // Verify that sender has permissions to create nodes
if (!service.canCreateNode(from) || (!UserManager.getInstance().isRegisteredUser(from) && !isComponent(from)) ) { if (!service.canCreateNode(from) || (!UserManager.getInstance().isRegisteredUser(from) && !isComponent(from)) ) {
// The user is not allowed to create nodes so return an error // The user is not allowed to create nodes so return an error
sendErrorPacket(iq, PacketError.Condition.forbidden, null); return new CreateNodeResponse(PacketError.Condition.forbidden, null, null);
return;
} }
DataForm completedForm = null; DataForm completedForm = null;
CollectionNode parentNode = null; CollectionNode parentNode = null;
...@@ -1102,8 +1172,7 @@ public class PubSubEngine { ...@@ -1102,8 +1172,7 @@ public class PubSubEngine {
// Instant nodes creation is not allowed so return an error // Instant nodes creation is not allowed so return an error
Element pubsubError = DocumentHelper.createElement( Element pubsubError = DocumentHelper.createElement(
QName.get("nodeid-required", "http://jabber.org/protocol/pubsub#errors")); QName.get("nodeid-required", "http://jabber.org/protocol/pubsub#errors"));
sendErrorPacket(iq, PacketError.Condition.not_acceptable, pubsubError); return new CreateNodeResponse(PacketError.Condition.not_acceptable, pubsubError, null);
return;
} }
do { do {
// Create a new nodeID and make sure that the random generated string does not // Create a new nodeID and make sure that the random generated string does not
...@@ -1129,13 +1198,11 @@ public class PubSubEngine { ...@@ -1129,13 +1198,11 @@ public class PubSubEngine {
Node tempNode = service.getNode(parentNodeID); Node tempNode = service.getNode(parentNodeID);
if (tempNode == null) { if (tempNode == null) {
// Requested parent node was not found so return an error // Requested parent node was not found so return an error
sendErrorPacket(iq, PacketError.Condition.item_not_found, null); return new CreateNodeResponse(PacketError.Condition.item_not_found, null, null);
return;
} }
else if (!tempNode.isCollectionNode()) { else if (!tempNode.isCollectionNode()) {
// Requested parent node is not a collection node so return an error // Requested parent node is not a collection node so return an error
sendErrorPacket(iq, PacketError.Condition.not_acceptable, null); return new CreateNodeResponse(PacketError.Condition.not_acceptable, null, null);
return;
} }
parentNode = (CollectionNode) tempNode; parentNode = (CollectionNode) tempNode;
} }
...@@ -1158,8 +1225,7 @@ public class PubSubEngine { ...@@ -1158,8 +1225,7 @@ public class PubSubEngine {
Node existingNode = service.getNode(newNodeID); Node existingNode = service.getNode(newNodeID);
if (existingNode != null) { if (existingNode != null) {
// There is a conflict since a node with the same ID already exists // There is a conflict since a node with the same ID already exists
sendErrorPacket(iq, PacketError.Condition.conflict, null); return new CreateNodeResponse(PacketError.Condition.conflict, null, null);
return;
} }
if (collectionType && !service.isCollectionNodesSupported()) { if (collectionType && !service.isCollectionNodesSupported()) {
...@@ -1167,24 +1233,21 @@ public class PubSubEngine { ...@@ -1167,24 +1233,21 @@ public class PubSubEngine {
Element pubsubError = DocumentHelper.createElement( Element pubsubError = DocumentHelper.createElement(
QName.get("unsupported", "http://jabber.org/protocol/pubsub#errors")); QName.get("unsupported", "http://jabber.org/protocol/pubsub#errors"));
pubsubError.addAttribute("feature", "collections"); pubsubError.addAttribute("feature", "collections");
sendErrorPacket(iq, PacketError.Condition.feature_not_implemented, pubsubError); return new CreateNodeResponse(PacketError.Condition.feature_not_implemented, pubsubError, null);
return;
} }
if (parentNode != null && !collectionType) { if (parentNode != null && !collectionType) {
// Check if requester is allowed to add a new leaf child node to the parent node // Check if requester is allowed to add a new leaf child node to the parent node
if (!parentNode.isAssociationAllowed(from)) { if (!parentNode.isAssociationAllowed(from)) {
// User is not allowed to add child leaf node to parent node. Return an error. // User is not allowed to add child leaf node to parent node. Return an error.
sendErrorPacket(iq, PacketError.Condition.forbidden, null); return new CreateNodeResponse(PacketError.Condition.forbidden, null, null);
return;
} }
// Check if number of child leaf nodes has not been exceeded // Check if number of child leaf nodes has not been exceeded
if (parentNode.isMaxLeafNodeReached()) { if (parentNode.isMaxLeafNodeReached()) {
// Max number of child leaf nodes has been reached. Return an error. // Max number of child leaf nodes has been reached. Return an error.
Element pubsubError = DocumentHelper.createElement(QName.get("max-nodes-exceeded", Element pubsubError = DocumentHelper.createElement(QName.get("max-nodes-exceeded",
"http://jabber.org/protocol/pubsub#errors")); "http://jabber.org/protocol/pubsub#errors"));
sendErrorPacket(iq, PacketError.Condition.conflict, pubsubError); return new CreateNodeResponse(PacketError.Condition.conflict, pubsubError, null);
return;
} }
} }
...@@ -1213,7 +1276,7 @@ public class PubSubEngine { ...@@ -1213,7 +1276,7 @@ public class PubSubEngine {
newNode.saveToDB(); newNode.saveToDB();
} }
CacheFactory.doClusterTask(new RefreshNodeTask(newNode)); CacheFactory.doClusterTask(new RefreshNodeTask(newNode));
} }
else { else {
conflict = true; conflict = true;
...@@ -1221,23 +1284,16 @@ public class PubSubEngine { ...@@ -1221,23 +1284,16 @@ public class PubSubEngine {
} }
if (conflict) { if (conflict) {
// There is a conflict since a node with the same ID already exists // There is a conflict since a node with the same ID already exists
sendErrorPacket(iq, PacketError.Condition.conflict, null); return new CreateNodeResponse(PacketError.Condition.conflict, null, null);
} }
else { else {
// Return success to the node owner // Return success to the node owner
IQ reply = IQ.createResultIQ(iq); return new CreateNodeResponse(null, null, newNode);
// Include new nodeID if it has changed from the original nodeID
if (!newNode.getNodeID().equals(nodeID)) {
Element elem =
reply.setChildElement("pubsub", "http://jabber.org/protocol/pubsub");
elem.addElement("create").addAttribute("node", newNode.getNodeID());
}
router.route(reply);
} }
} }
catch (NotAcceptableException e) { catch (NotAcceptableException e) {
// Node should have at least one owner. Return not-acceptable error. // Node should have at least one owner. Return not-acceptable error.
sendErrorPacket(iq, PacketError.Condition.not_acceptable, null); return new CreateNodeResponse(PacketError.Condition.not_acceptable, null, null);
} }
} }
......
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