File

mod_throttle_unsolicited/mod_throttle_unsolicited.lua @ 5418:f2c7bb3af600

mod_http_oauth2: Add role selector to consent page List includes all roles available to the user, if more than one. Defaults to either the first role in the scope string or the users primary role. Earlier draft listed all roles, but having options that can't be selected is bad UX and the entire list of all roles on the server could be long, and perhaps even sensitive. Allows e.g. picking a role with fewer permissions than what might otherwise have been selected. UX wise, doing this with more checkboxes or possibly radio buttons would have been confusion and/or looked messier. Fixes the previous situation where unselecting a role would default to the primary role, which could be more permissions than requested.
author Kim Alvefur <zash@zash.se>
date Fri, 05 May 2023 01:23:13 +0200
parent 3541:4d8a68557941
line wrap: on
line source

local st = require"util.stanza";
local jid_split = require "util.jid".split;
local jid_bare = require "util.jid".bare;
local is_contact_subscribed = require "core.rostermanager".is_contact_subscribed;
local throttle = require "util.throttle";
local gettime = require "socket".gettime;

local max = module:get_option_number("unsolicited_messages_per_minute", 10);
local s2s_max = module:get_option_number("unsolicited_s2s_messages_per_minute");
local multiplier = module:get_option_number("throttle_unsolicited_burst", 1);

function check_subscribed(event)
	local stanza, origin = event.stanza, event.origin;
	local log = origin.log or module._log;
	if stanza.attr.type == "error" then return end

	local to_orig = stanza.attr.to;
	if to_orig == nil or to_orig == origin.full_jid then return end -- to self

	local to_bare = jid_bare(to_orig);
	local from_jid = jid_bare(stanza.attr.from);
	if to_bare == from_jid then return end -- to own resource

	-- Check if it's a message to a joined room
	local rooms = origin.rooms_joined;
	if rooms and rooms[to_bare] then
		return
	end

	-- Retrieve or create throttle object
	local lim = origin.throttle_unsolicited;
	if not lim then
		lim = throttle.create(max * multiplier, 60 * multiplier);
		origin.throttle_unsolicited = lim;
	end

	local to_user, to_host = jid_split(to_orig);
	if to_user and not is_contact_subscribed(to_user, to_host, from_jid) then
		log("debug", "[unsolicited] %s is not subscribed to %s@%s", from_jid, to_user, to_host);
		if not lim:poll(1) then
			log("warn", "[unsolicited] Sent too many messages to non-contacts, bouncing message");
			origin.firewall_mark_unsolicited = gettime();
			origin.send(st.error_reply(stanza, "cancel", "service-unavailable"));
			return true;
		end
	end
end

module:hook("pre-message/bare", check_subscribed, 200);
module:hook("pre-message/full", check_subscribed, 200);

local full_sessions = prosody.full_sessions;

-- Rooms and throttle creation will differ for s2s
function check_subscribed_s2s(event)
	local stanza, origin = event.stanza, event.origin;
	local log = origin.log or module._log;

	if origin.type ~= "s2sin" then return end

	local to_orig = stanza.attr.to;
	local from_orig = stanza.attr.from;
	local from_bare = jid_bare(from_orig);

	local target = full_sessions[to_orig];
	if target then
		local rooms = target.rooms_joined;
		if rooms and rooms[from_bare] then
			log("debug", "Message to joined room, no limit");
			return
		end
	end

	-- Retrieve or create throttle object
	local lim = origin.throttle_unsolicited;
	if not lim then
		log("debug", "New s2s throttle");
		lim = throttle.create(s2s_max * multiplier, 60 * multiplier);
		origin.throttle_unsolicited = lim;
	end

	return check_subscribed(event);
end

if s2s_max then
	module:hook("message/bare", check_subscribed_s2s, 200);
	module:hook("message/full", check_subscribed_s2s, 200);
end

module:depends("track_muc_joins");