view mod_sift/mod_sift.lua @ 6214:cc5c0f1dc89b

mod_muc_anonymize_moderation_actions: Fix mentioning Virtualhost for MUC, Compatibility section and License. diff --git a/mod_muc_anonymize_moderation_actions/README.md b/mod_muc_anonymize_moderation_actions/README.md --- a/mod_muc_anonymize_moderation_actions/README.md +++ b/mod_muc_anonymize_moderation_actions/README.md @@ -1,8 +1,10 @@ -<!-- -SPDX-FileCopyrightText: 2024 John Livingston <https://www.john-livingston.fr/> -SPDX-License-Identifier: AGPL-3.0-only ---> -# mod_muc_anonymize_moderation_actions +--- +labels: +- 'Stage-Alpha' +summary: Anonymize moderator actions for participants +--- + +## Introduction This modules allows to anonymize affiliation and role changes in MUC rooms. @@ -11,13 +13,10 @@ When the feature is enabled, when a mode This is particularly usefull to prevent some revenge when a moderator bans someone. -This module is under AGPL-3.0 license. - -It was tested on Prosody 0.12.x. ## Configuration -Just enable the module on your MUC VirtualHost. +Just enable the module on your MUC Component. The feature will be accessible throught the room configuration form. You can tweak the position of the settings in the MUC configuration form using `anonymize_moderation_actions_form_position`. @@ -26,7 +25,20 @@ This value will be passed as priority fo By default, the field will be between muc#roomconfig_changesubject and muc#roomconfig_moderatedroom (default value is `78`). ``` lua -VirtualHost "muc.example.com" +Component "muc.example.com" "muc" modules_enabled = { "muc_anonymize_moderation_actions" } anonymize_moderation_actions_form_position = 96 ``` + +## Compatibility + + ------ ---------------------- + trunk Works as of 25-05-12 + 0.13 Works + 0.12 Works + ------ ---------------------- + +### License + +SPDX-FileCopyrightText: 2024 John Livingston <https://www.john-livingston.fr/> +SPDX-License-Identifier: AGPL-3.0-only
author Menel <menel@snikket.de>
date Mon, 12 May 2025 10:42:26 +0200
parents 7dbde05b48a9
children
line wrap: on
line source


local st = require "util.stanza";
local jid_bare = require "util.jid".bare;

-- advertise disco features
module:add_feature("urn:xmpp:sift:1");

-- supported features
module:add_feature("urn:xmpp:sift:stanzas:iq");
module:add_feature("urn:xmpp:sift:stanzas:message");
module:add_feature("urn:xmpp:sift:stanzas:presence");
module:add_feature("urn:xmpp:sift:recipients:all");
module:add_feature("urn:xmpp:sift:senders:all");

-- allowed values of 'sender' and 'recipient' attributes
local senders = {
	["all"] = true;
	["local"] = true;
	["others"] = true;
	["remote"] = true;
	["self"] = true;
};
local recipients = {
	["all"] = true;
	["bare"] = true;
	["full"] = true;
};

-- this function converts a <message/>, <presence/> or <iq/> element in
-- the SIFT namespace into a hashtable, for easy lookup
local function to_hashtable(element)
	if element ~= nil then
		local hash = {};
		-- make sure the sender and recipient attributes has a valid value
		hash.sender = element.attr.sender or "all";
		if not senders[hash.sender] then return false; end -- bad value, returning false
		hash.recipient = element.attr.recipient or "all";
		if not recipients[hash.recipient] then return false; end -- bad value, returning false
		-- next we loop over all <allow/> elements
		for _, tag in ipairs(element) do
			if tag.name == "allow" and tag.attr.xmlns == "urn:xmpp:sift:1" then
				-- make sure the element is valid
				if not tag.attr.name or not tag.attr.ns then return false; end -- missing required attributes, returning false
				hash[tag.attr.ns.."|"..tag.attr.name] = true;
				hash.allowed = true; -- just a flag indicating we have some elements allowed
			end
		end
		return hash;
	end
end

local data = {}; -- table with all our data

-- handle SIFT set
module:hook("iq/self/urn:xmpp:sift:1:sift", function(event)
	local origin, stanza = event.origin, event.stanza;
	if stanza.attr.type == "set" then
		local sifttag = stanza.tags[1]; -- <sift/>

		-- first, get the elements we are interested in
		local message = sifttag:get_child("message");
		local presence = sifttag:get_child("presence");
		local iq = sifttag:get_child("iq");

		-- for quick lookup, convert the elements into hashtables
		message = to_hashtable(message);
		presence = to_hashtable(presence);
		iq = to_hashtable(iq);

		-- make sure elements were valid
		if message == false or presence == false or iq == false then
			origin.send(st.error_reply(stanza, "modify", "bad-request"));
			return true;
		end

		local existing = data[origin.full_jid] or {}; -- get existing data, if any
		data[origin.full_jid] = { presence = presence, message = message, iq = iq }; -- store new data

		origin.send(st.reply(stanza)); -- send back IQ result

		if not existing.presence and not origin.presence and presence then
			-- TODO send probes
		end
		return true;
	end
end);

-- handle user disconnect
module:hook("resource-unbind", function(event)
	data[event.session.full_jid] = nil; -- discard data
end);

-- IQ handler
module:hook("iq/full", function(event)
	local origin, stanza = event.origin, event.stanza;
	local siftdata = data[stanza.attr.to];
	if stanza.attr.type == "get" or stanza.attr.type == "set" then
		if siftdata and siftdata.iq then -- we seem to have an IQ filter
			local tag = stanza.tags[1]; -- the IQ child
			if not siftdata.iq[(tag.attr.xmlns or "jabber:client").."|"..tag.name] then
				-- element not allowed; sending back generic error
				origin.send(st.error_reply(stanza, "cancel", "service-unavailable"));
				return true;
			end
		end
	end
end, 50);

-- Message to full JID handler
module:hook("message/full", function(event)
	local origin, stanza = event.origin, event.stanza;
	local siftdata = data[stanza.attr.to];
	if siftdata and siftdata.message then -- we seem to have an message filter
		local allowed = false;
		for _, childtag in ipairs(stanza.tags) do
			if siftdata.message[(childtag.attr.xmlns or "jabber:client").."|"..childtag.name] then
				allowed = true;
			end
		end
		if not allowed then
			-- element not allowed; sending back generic error
			origin.send(st.error_reply(stanza, "cancel", "service-unavailable"));
			-- FIXME maybe send to offline storage
			return true;
		end
	end
end, 50);

-- Message to bare JID handler
module:hook("message/bare", function(event)
	local origin, stanza = event.origin, event.stanza;
	local user = bare_sessions[jid_bare(stanza.attr.to)];
	local allowed = false;
	for _, session in pairs(user and user.sessions or {}) do
		local siftdata = data[session.full_jid];
		if siftdata and siftdata.message then -- we seem to have an message filter
			for _, childtag in ipairs(stanza.tags) do
				if siftdata.message[(childtag.attr.xmlns or "jabber:client").."|"..childtag.name] then
					allowed = true;
				end
			end
		else
			allowed = true;
		end
	end
	if user and not allowed then
		-- element not allowed; sending back generic error
		origin.send(st.error_reply(stanza, "cancel", "service-unavailable"));
		-- FIXME maybe send to offline storage
		return true;
	end
end, 50);

-- Presence to full JID handler
module:hook("presence/full", function(event)
	local origin, stanza = event.origin, event.stanza;
	local siftdata = data[stanza.attr.to];
	if siftdata and siftdata.presence then -- we seem to have an presence filter
		local allowed = false;
		for _, childtag in ipairs(stanza.tags) do
			if siftdata.presence[(childtag.attr.xmlns or "jabber:client").."|"..childtag.name] then
				allowed = true;
			end
		end
		if not allowed then
			-- element not allowed; sending back generic error
			--origin.send(st.error_reply(stanza, "cancel", "service-unavailable"));
			return true;
		end
	end
end, 50);

-- Presence to bare JID handler
module:hook("presence/bare", function(event)
	local origin, stanza = event.origin, event.stanza;
	local user = bare_sessions[jid_bare(stanza.attr.to)];
	local allowed = false;
	for _, session in pairs(user and user.sessions or {}) do
		local siftdata = data[session.full_jid];
		if siftdata and siftdata.presence then -- we seem to have an presence filter
			for _, childtag in ipairs(stanza.tags) do
				if siftdata.presence[(childtag.attr.xmlns or "jabber:client").."|"..childtag.name] then
					allowed = true;
				end
			end
		else
			allowed = true;
		end
	end
	if user and not allowed then
		-- element not allowed; sending back generic error
		--origin.send(st.error_reply(stanza, "cancel", "service-unavailable"));
		return true;
	end
end, 50);