view mod_component_roundrobin/mod_component_roundrobin.lua @ 5170:4d6af8950016

mod_muc_moderation: Derive role from reserved nickname if occupant When using a different client to moderate than the one used to participate in the chat, e.g. a command line tool like clix, there's no occupant and no role to use in the permission check. Previously the default role based on affiliation was used. Now if you are present in the room using your reserved nick, the role you have there is used in the permission check instead of the default affiliation-derived role.
author Kim Alvefur <zash@zash.se>
date Sun, 19 Feb 2023 18:17:37 +0100
parents 7dbde05b48a9
children
line wrap: on
line source

-- Prosody IM
-- Copyright (C) 2008-2010 Matthew Wild
-- Copyright (C) 2008-2010 Waqas Hussain
--
-- This project is MIT/X11 licensed. Please see the
-- COPYING file in the source package for more information.
--

if module:get_host_type() ~= "component" then
	error("Don't load mod_component manually, it should be for a component, please see http://prosody.im/doc/components", 0);
end

local hosts = _G.hosts;

local t_concat = table.concat;

local sha1 = require "util.hashes".sha1;
local st = require "util.stanza";

local log = module._log;

local sessions = module:shared("sessions");

local last_session;
local function on_destroy(session, err)
	if sessions[session] then
		if last_session == session then last_session = nil; end
		sessions[session] = nil;
		session.on_destroy = nil;
	end
end

local function handle_stanza(event)
	local stanza = event.stanza;
	if next(sessions) then
		stanza.attr.xmlns = nil;
		last_session = next(sessions, last_session) or next(sessions);
		last_session.send(stanza);
	else
		log("warn", "Component not connected, bouncing error for: %s", stanza:top_tag());
		if stanza.attr.type ~= "error" and stanza.attr.type ~= "result" then
			event.origin.send(st.error_reply(stanza, "wait", "service-unavailable", "Component unavailable"));
		end
	end
	return true;
end

module:hook("iq/bare", handle_stanza, -0.5);
module:hook("message/bare", handle_stanza, -0.5);
module:hook("presence/bare", handle_stanza, -0.5);
module:hook("iq/full", handle_stanza, -0.5);
module:hook("message/full", handle_stanza, -0.5);
module:hook("presence/full", handle_stanza, -0.5);
module:hook("iq/host", handle_stanza, -0.5);
module:hook("message/host", handle_stanza, -0.5);
module:hook("presence/host", handle_stanza, -0.5);

--- Handle authentication attempts by components
function handle_component_auth(event)
	local session, stanza = event.origin, event.stanza;

	if session.type ~= "component_unauthed" then return; end
	if sessions[session] then return; end

	if (not session.host) or #stanza.tags > 0 then
		(session.log or log)("warn", "Invalid component handshake for host: %s", session.host);
		session:close("not-authorized");
		return true;
	end

	local secret = module:get_option("component_secret");
	if not secret then
		(session.log or log)("warn", "Component attempted to identify as %s, but component_secret is not set", session.host);
		session:close("not-authorized");
		return true;
	end

	local supplied_token = t_concat(stanza);
	local calculated_token = sha1(session.streamid..secret, true);
	if supplied_token:lower() ~= calculated_token:lower() then
		log("info", "Component authentication failed for %s", session.host);
		session:close{ condition = "not-authorized", text = "Given token does not match calculated token" };
		return true;
	end

	-- Add session to sessions table
	sessions[session] = true;
	session.on_destroy = on_destroy;
	session.component_validate_from = module:get_option_boolean("validate_from_addresses", true);
	session.type = "component";
	log("info", "Component successfully authenticated: %s", session.host);
	session.send(st.stanza("handshake"));

	return true;
end

module:hook("stanza/jabber:component:accept:handshake", handle_component_auth, 10);