changeset 14116:c8458864dcea

util.pubsub: Allow publishers to retract items they published This requires the service configuration to provide an iteminfo() callback which returns a table of metadata including a 'publisher' field. Retraction is permitted only if the JID (currently) has permission to publish, and they were the publisher of the item.
author Matthew Wild <mwild1@gmail.com>
date Tue, 31 Mar 2026 14:13:32 +0100
parents 44a8c8d4146a
children 354427afb638
files util/pubsub.lua
diffstat 1 files changed, 28 insertions(+), 3 deletions(-) [+]
line wrap: on
line diff
--- a/util/pubsub.lua	Tue Mar 31 12:25:20 2026 +0100
+++ b/util/pubsub.lua	Tue Mar 31 14:13:32 2026 +0100
@@ -9,6 +9,7 @@
 	broadcaster = function () end;
 	subscriber_filter = function (subs) return subs end;
 	itemcheck = function () return true; end;
+	iteminfo = function () return nil; end;
 	get_affiliation = function () end;
 	normalize_jid = function (jid) return jid; end;
 	metadata_subset = {};
@@ -596,8 +597,21 @@
 
 function service:retract(node, actor, id, retract) --> ok, err
 	-- Access checking
-	if not self:may(node, actor, "retract") then
-		return false, "forbidden";
+	local may_retract = self:may(node, actor, "retract");
+
+	if not may_retract then
+		local node_obj = self.nodes[node];
+		local publish_model = node_obj and node_obj.config.publish_model;
+		local may_publish = (
+			publish_model == "open"
+			or (publish_model == "subscribers" and node_obj.subscribers[actor])
+		);
+		if not may_publish then
+			return false, "forbidden";
+		end
+		-- Otherwise, we continue on, with may_retract = false, which
+		-- indicates they cannot retract items published by *others*
+		-- but may still retract their own items
 	end
 	--
 	local node_obj = self.nodes[node];
@@ -605,9 +619,20 @@
 		return false, "item-not-found";
 	end
 	if self.data[node] then
-		if not self.data[node]:get(id) then
+		local item = self.data[node]:get(id);
+		if not item then
 			return false, "item-not-found";
 		end
+
+		if not may_retract then
+			-- May not generally retract all items, so check if they are the publisher
+			local info = self.config.iteminfo(item);
+			local is_publisher = info and info.publisher and self:jids_equal(info.publisher, actor);
+			if not is_publisher then
+				return false, "forbidden";
+			end
+		end
+
 		local ok = self.data[node]:set(id, nil);
 		if not ok then
 			return nil, "internal-server-error";