diff --git a/resources/prosody-plugins/mod_filter_iq_rayo.lua b/resources/prosody-plugins/mod_filter_iq_rayo.lua index 6b69c90c4..a71af0692 100644 --- a/resources/prosody-plugins/mod_filter_iq_rayo.lua +++ b/resources/prosody-plugins/mod_filter_iq_rayo.lua @@ -10,6 +10,13 @@ if token_util == nil then return; end +-- configuration to limit number of outgoing calls +local LIMIT_OUTGOING_CALLS = module:get_option_number("max_number_outgoing_calls", -1); + +-- Header names to use to push extra data extracted from token, if any +local OUT_INITIATOR_USER_ATTR_NAME = "X-outbound-call-initiator-user"; +local OUT_INITIATOR_GROUP_ATTR_NAME = "X-outbound-call-initiator-group"; + -- filters rayo iq in case of requested from not jwt authenticated sessions -- or if the session has features in user context and it doesn't mention -- feature "outbound-call" to be enabled @@ -39,11 +46,120 @@ module:hook("pre-iq/full", function(event) (dial.attr.to == 'jitsi_meet_transcribe' and 'transcription' or 'outbound-call')) then - module:log("info", + module:log("warn", "Filtering stanza dial, stanza:%s", tostring(stanza)); session.send(st.error_reply(stanza, "auth", "forbidden")); return true; end + + -- now lets check any limits if configured + if LIMIT_OUTGOING_CALLS > 0 + and get_concurrent_outgoing_count( + session.jitsi_meet_context_user["id"], + session.jitsi_meet_context_group) >= LIMIT_OUTGOING_CALLS + then + module:log("warn", + "Filtering stanza dial, stanza:%s, outgoing calls limit reached", tostring(stanza)); + session.send(st.error_reply(stanza, "cancel", "resource-constraint")); + return true; + end + + -- now lets insert token information if any + if session and session.jitsi_meet_context_user then + -- First remove any 'header' element if it already + -- exists, so it cannot be spoofed by a client + stanza:maptags( + function(tag) + if tag.name == "header" + and (tag.attr.name == OUT_INITIATOR_USER_ATTR_NAME + or tag.attr.name == OUT_INITIATOR_GROUP_ATTR_NAME) then + return nil + end + return tag + end + ) + + local dial = stanza:get_child('dial', 'urn:xmpp:rayo:1'); + -- adds initiator user id from token + dial:tag("header", { + xmlns = "urn:xmpp:rayo:1", + name = OUT_INITIATOR_USER_ATTR_NAME, + value = session.jitsi_meet_context_user["id"] }); + dial:up(); + + -- Add the initiator group information if it is present + if session.jitsi_meet_context_group then + dial:tag("header", { + xmlns = "urn:xmpp:rayo:1", + name = OUT_INITIATOR_GROUP_ATTR_NAME, + value = session.jitsi_meet_context_group }); + dial:up(); + end + end end end end); + +--- Finds and returns the number of concurrent outgoing calls for a user +-- @param context_user the user id extracted from the token +-- @param context_group the group id extracted from the token +-- @return returns the count of concurrent calls +function get_concurrent_outgoing_count(context_user, context_group) + local count = 0; + for _, host in pairs(hosts) do + local component = host; + if component then + local muc = component.modules.muc + local rooms = nil; + if muc and rawget(muc,"rooms") then + -- We're running 0.9.x or 0.10 (old MUC API) + return muc.rooms; + elseif muc and rawget(muc,"live_rooms") then + -- We're running >=0.11 (new MUC API) + rooms = muc.live_rooms(); + elseif muc and rawget(muc,"each_room") then + -- We're running trunk<0.11 (each_room is later [DEPRECATED]) + rooms = muc.each_room(true); + end + + -- now lets iterate over rooms and occupants and search for + -- call initiated by the user + if rooms then + for room in rooms do + for _, occupant in room:each_occupant() do + for _, presence in occupant:each_session() do + + local initiator = presence:get_child('initiator', 'http://jitsi.org/protocol/jigasi'); + + local found_user = false; + local found_group = false; + + if initiator then + initiator:maptags(function (tag) + if tag.name == "header" + and tag.attr.name == OUT_INITIATOR_USER_ATTR_NAME then + found_user = tag.attr.value == context_user; + elseif tag.name == "header" + and tag.attr.name == OUT_INITIATOR_GROUP_ATTR_NAME then + found_group = tag.attr.value == context_group; + end + + return tag; + end ); + -- if found a jigasi participant initiated by the concurrent + -- participant, count it + if found_user + and (context_group == nil or found_group) then + count = count + 1; + end + end + end + end + end + end + end + end + + return count; +end +