diff --git a/ansible/roles/prosody/files/mod_muc_auth_vpaas.lua b/ansible/roles/prosody/files/mod_muc_auth_vpaas.lua index efa1be74..922ae416 100644 --- a/ansible/roles/prosody/files/mod_muc_auth_vpaas.lua +++ b/ansible/roles/prosody/files/mod_muc_auth_vpaas.lua @@ -1,3 +1,4 @@ +-- Loaded only under main muc module local json_safe = require "cjson.safe"; local basexx = require "basexx"; local cache = require "util.cache"; @@ -5,6 +6,8 @@ local cache = require "util.cache"; local st = require "util.stanza"; local timer = require "util.timer"; +local um_is_admin = require "core.usermanager".is_admin; + local inspect = require('inspect'); local util_internal = module:require "util.internal"; @@ -44,6 +47,10 @@ local token_util = module:require "token/util".new(parentCtx); local DEBUG = false; +local function is_admin(jid) + return um_is_admin(jid, module.host); +end + function invalidate_cache() token_util:clear_asap_cache() return CACHE_EXPIRATION_SECONDS; @@ -161,62 +168,65 @@ local function deny_access(origin, stanza, room_disabled_access, room, occupant) local room_jid = room.jid; local token = origin.auth_token; local tenant = origin.jitsi_meet_domain; - if not is_healthcheck_room(room_jid) and not util_internal.is_blacklisted(occupant) then - local initiator = stanza:get_child('initiator', 'http://jitsi.org/protocol/jigasi'); - if initiator then - if DEBUG then module:log("debug", "Let Jigasi pass throw"); end - return nil; - end - if util.is_sip_jibri_join(stanza) then - module:log("info", "Let Sip Jibri pass through %s", occupant); - return nil; - end + if is_healthcheck_room(room_jid) + or is_admin(occupant.bare_jid) - if not is_vpaas(room) then - if DEBUG then module:log("debug", "Skip VPAAS related verifications for non VPAAS room %s", room_jid); end - return nil; - end + -- Skip VPAAS related verifications for non VPAAS room + or not is_vpaas(room) - if DEBUG then module:log("debug", - "Will verify if VPAAS room: %s has token on user %s pre-join", room_jid, occupant); end - -- we allow participants from the main prosody to connect without token to the visitor one - if token == nil and origin.type ~= 's2sin' then - module:log("warn", "VPAAS room %s does not have a token", room_jid); - origin.send(st.error_reply(stanza, "cancel", "not-allowed", "VPAAS room disabled for guests")); - return true; - end + -- Let Jigasi or transcriber pass throw + or util.is_sip_jigasi(stanza) + or util.is_transcriber_jigasi(stanza) - if token ~= nil and not starts_with(tenant, VPAAS_PREFIX) then - if room._data.vpaas_guest_access then - -- make sure it is not authenticated user, a guest (no features are set) - origin.auth_token = nil; - origin.jitsi_meet_room = nil; - origin.jitsi_meet_domain = nil; - origin.jitsi_meet_str_tenant = nil; - origin.jitsi_meet_context_user = nil; - origin.jitsi_meet_context_group = nil; - origin.jitsi_meet_context_features = nil; - origin.jitsi_meet_context_room = nil; - origin.contextRequired = nil; - origin.public_key = nil; - origin.kid = nil; - -- let's mark this session that we cleared the token - origin.vpaas_guest_access = true; - - return nil; - end - - module:log("warn", "VPAAS room %s is disabled for tenant %s", room_jid, tenant); - origin.send(st.error_reply(stanza, "cancel", "not-allowed", "VPAAS room disabled for 8x8 users")); - return true; - end + -- is jibri + or util.is_jibri(occupant) - if room_disabled_access then - module:log("warn", "VPAAS room %s has access disabled due to blocked or deleted tenant %s", room_jid, tenant); - origin.send(st.error_reply(stanza, "cancel", "not-allowed", "VPAAS room disabled due to blocked or deleted tenant")); - return true; + -- Let Sip Jibri pass through + or util.is_sip_jibri_join(stanza) then + return nil; + end + + if DEBUG then module:log("debug", + "Will verify if VPAAS room: %s has token on user %s pre-join", room_jid, occupant); end + + -- we allow participants from the main prosody to connect without token to the visitor one + if token == nil and origin.type ~= 's2sin' then + module:log("warn", "VPAAS room %s does not have a token", room_jid); + origin.send(st.error_reply(stanza, "cancel", "not-allowed", "VPAAS room disabled for guests")); + return true; + end + + -- This is the case when a participant with a valid token (8x8) access a jaas room, we want it to join as a guest + if token ~= nil and not starts_with(tenant, VPAAS_PREFIX) then + if room._data.vpaas_guest_access then + -- make sure it is not authenticated user, a guest (no features are set) + origin.auth_token = nil; + origin.jitsi_meet_room = nil; + origin.jitsi_meet_domain = nil; + origin.jitsi_meet_str_tenant = nil; + origin.jitsi_meet_context_user = nil; + origin.jitsi_meet_context_group = nil; + origin.jitsi_meet_context_features = nil; + origin.jitsi_meet_context_room = nil; + origin.contextRequired = nil; + origin.public_key = nil; + origin.kid = nil; + -- let's mark this session that we cleared the token + origin.vpaas_guest_access = true; + + return nil; end + + module:log("warn", "VPAAS room %s is disabled for tenant %s", room_jid, tenant); + origin.send(st.error_reply(stanza, "cancel", "not-allowed", "VPAAS room disabled for 8x8 users")); + return true; + end + + if room_disabled_access then + module:log("warn", "VPAAS room %s has access disabled due to blocked or deleted tenant %s", room_jid, tenant); + origin.send(st.error_reply(stanza, "cancel", "not-allowed", "VPAAS room disabled due to blocked or deleted tenant")); + return true; end return nil; diff --git a/ansible/roles/prosody/files/mod_muc_events.lua b/ansible/roles/prosody/files/mod_muc_events.lua index aed35b67..859d90e3 100644 --- a/ansible/roles/prosody/files/mod_muc_events.lua +++ b/ansible/roles/prosody/files/mod_muc_events.lua @@ -1,5 +1,6 @@ module:set_global(); +local um_is_admin = require "core.usermanager".is_admin; local jid = require "util.jid"; local http = require "net.http"; local json = require "cjson"; @@ -8,8 +9,11 @@ local socket = require "socket"; local uuid_gen = require "util.uuid".generate; local jwt = module:require "luajwtjitsi"; local util = module:require "util.internal"; -local is_healthcheck_room = module:require "util".is_healthcheck_room; -local is_vpaas = module:require "util".is_vpaas; +local oss_util = module:require "util"; +local is_healthcheck_room = oss_util.is_healthcheck_room; +local is_jibri = oss_util.is_jibri; +local is_vpaas = oss_util.is_vpaas; +local is_transcriber_jigasi = oss_util.is_transcriber_jigasi; local event_count = module:measure("muc_events_rate", "rate") local event_count_failed = module:measure("muc_events_failed", "rate") @@ -22,10 +26,6 @@ local function onConfCacheEvict(evictedKey, evictedValue) end local confCache = require"util.cache".new(confCacheSize, onConfCacheEvict); --- option to ignore events about the focus and other components -local blacklistPrefixes - = module:get_option_array("muc_events_blacklist_prefixes", {'focus@auth.','recorder@recorder.','jibria@recorder.','jibrib@','jvb@auth.','jibri@auth.','jibria@auth.','jibrib@auth.','transcriber@recorder.', 'transcribera@recorder.', 'transcriberb@recorder.'}); - local eventURL = module:get_option_string("muc_events_url", 'http://127.0.0.1:9880/'); @@ -61,6 +61,10 @@ local escaped_muc_domain_prefix = muc_domain_prefix:gsub("%p", "%%%1"); local target_subdomain_pattern = "^"..escaped_muc_domain_prefix..".([^%.]+)%."..escaped_muc_domain_base; +local function is_admin(jid) + return um_is_admin(jid, module.host); +end + local function remove_from_cache(key) confCache:set(key, nil); end @@ -162,17 +166,6 @@ local function extract_occupant_details(occupant) return r; end -local function isBlacklisted(occupant) - for i, bPrefix in ipairs(blacklistPrefixes) do - if string.sub(occupant.bare_jid,1,string.len(bPrefix)) == bPrefix then - if DEBUG then module:log("debug","Blacklist prefix: %s found in %s ", bPrefix, occupant); end - return true; - end - end - - return false; -end - local function cb(content_, code_, response_, request_) if code_ == 200 or code_ == 204 then if DEBUG then module:log("debug", "URL Callback: Code %s, Content %s, Request (host %s, path %s, body %s), Response: %s", @@ -285,15 +278,10 @@ local function processJoinLeftEvent(type,event) if DEBUG then module:log("debug", "%s keys in confCache", confCache:count()); end local who = event.occupant; - -- search bare_jid for blacklisted prefixes before sending events - if isBlacklisted(who) then - if DEBUG then module:log("debug", "processJoinLeftEvent: occupant is blacklisted %s", who); end - return; - end - - -- search room jid for tenancy prefixes before sending events - if is_vpaas(event.room) then - if DEBUG then module:log("debug", "processJoinLeftEvent: room tenant is droplisted %s", event.room.jid); end + if is_admin(who.bare_jid) + or is_vpaas(event.room) + or is_jibri(who) + or is_transcriber_jigasi(event.stanza) then return; end @@ -395,15 +383,10 @@ local function handleBroadcastPresence(event) local occupant_jid = occupant.jid; local room_jid = event.room.jid; - -- search bare_jid for blacklisted prefixes before broadcasting events - if isBlacklisted(occupant) then - if DEBUG then module:log("debug", "handleBroadcastPresence: occupant is blacklisted %s", occupant.bare_jid); end - return; - end - - -- search room jid for tenancy prefixes before sending events - if is_vpaas(event.room) then - if DEBUG then module:log("debug", "handleBroadcastPresence: room tenant is droplisted %s", room_jid); end + if is_admin(occupant.bare_jid) + or is_vpaas(event.room) + or is_jibri(occupant) + or is_transcriber_jigasi(event.stanza) then return; end @@ -444,8 +427,8 @@ end -- used only for vo local function processSubjectUpdate(occupant, room, new_subject) local room_jid = room.jid; - if is_vpaas(room) then - if DEBUG then module:log("debug", "processSubjectUpdate: room tenant is droplisted %s", room_jid); end + + if is_admin(occupant.bare_jid) or is_vpaas(room) then return; end @@ -462,12 +445,6 @@ local function processSubjectUpdate(occupant, room, new_subject) pdetails['jid'] = occupant.jid; pdetails['bare_jid'] = occupant.bare_jid; - -- search bare_jid for blacklisted prefixes before sending events - if isBlacklisted(occupant) then - if DEBUG then module:log("debug", "processSubjectUpdate occupant is blacklisted %s", occupant); end - return; - end - local cdetails = load_from_cache(room_jid); cdetails["subject"] = new_subject; store_in_cache(room_jid, cdetails); @@ -658,12 +635,10 @@ local function handleSpeakerStats(event) end for user_jid, speakerTime in pairs(event.roomSpeakerStats) do if (user_jid ~= "dominantSpeakerId" and user_jid ~= "sessionId" and user_jid ~= "isBreakout" and user_jid ~= "breakoutRoomId") then - if not util.is_blacklisted(user_jid) then - if speakerTime.context_user ~= nil then - requestBody.speakerStats[user_jid] = { time = speakerTime.totalDominantSpeakerTime; name = speakerTime.context_user.name; email = speakerTime.context_user.email; id = speakerTime.context_user.id; }; - else - requestBody.speakerStats[user_jid] = { time = speakerTime.totalDominantSpeakerTime;}; - end + if speakerTime.context_user ~= nil then + requestBody.speakerStats[user_jid] = { time = speakerTime.totalDominantSpeakerTime; name = speakerTime.context_user.name; email = speakerTime.context_user.email; id = speakerTime.context_user.id; }; + else + requestBody.speakerStats[user_jid] = { time = speakerTime.totalDominantSpeakerTime;}; end end end diff --git a/ansible/roles/prosody/files/mod_muc_permissions_vpaas.lua b/ansible/roles/prosody/files/mod_muc_permissions_vpaas.lua index 9474aceb..f9097c5f 100644 --- a/ansible/roles/prosody/files/mod_muc_permissions_vpaas.lua +++ b/ansible/roles/prosody/files/mod_muc_permissions_vpaas.lua @@ -113,7 +113,12 @@ module:hook("muc-occupant-pre-join", function(event) local room, session, stanza = event.room, event.origin, event.stanza; local occupant_jid = stanza.attr.from; - if is_healthcheck_room(room.jid) or util.is_blacklisted(occupant_jid) then + if is_healthcheck_room(room.jid) + or is_admin(occupant_jid) + or oss_util.is_sip_jigasi(stanza) + or oss_util.is_transcriber_jigasi(stanza) + or oss_util.is_jibri(occupant) + or oss_util.is_sip_jibri_join(stanza) then return; end diff --git a/ansible/roles/prosody/files/mod_muc_webhooks.lua b/ansible/roles/prosody/files/mod_muc_webhooks.lua index ba440980..166aa84c 100644 --- a/ansible/roles/prosody/files/mod_muc_webhooks.lua +++ b/ansible/roles/prosody/files/mod_muc_webhooks.lua @@ -1,3 +1,4 @@ +-- loaded for the muc components (the main, lobby and breakout room) local util = module:require "util.internal"; local uuid_gen = require "util.uuid".generate; local inspect = require('inspect'); @@ -73,7 +74,6 @@ local main_muc_service; local NICK_NS = "http://jabber.org/protocol/nick"; local JIGASI_CALL_DIRECTION_ATTR_NAME = "JigasiCallDirection"; local TRANSCRIBER_PREFIXES = { 'transcriber@recorder.', 'transcribera@recorder.', 'transcriberb@recorder.' }; -local RECORDER_PREFIXES = { 'recorder@recorder.', 'jibria@recorder.', 'jibrib@recorder.' }; local event_count = module:measure("muc_webhooks_rate", "rate") local event_count_failed = module:measure("muc_webhooks_failed", "rate") @@ -254,281 +254,294 @@ function handle_occupant_access(event, event_type) local final_event_type = event_type local dial_participants = room._data.dial_participants or {}; - if not is_healthcheck_room(room.jid) and (not util.is_blacklisted(occupant) or oss_util.starts_with_one_of(occupant.jid, TRANSCRIBER_PREFIXES) or oss_util.starts_with_one_of(occupant.jid, RECORDER_PREFIXES)) then - if DEBUG then - module:log("debug", "Will send participant event %s for room %s is_breakout (%s, main room jid:%s)", - occupant.jid, room.jid, is_breakout, main_room.jid); - end - local meeting_fqn, customer_id = util.get_fqn_and_customer_id(main_room); - local _, _, nick_resource = split_jid(occupant.nick); - local session = event.origin; - local payload = {}; - - if session and session.auth_token then - -- replace user name from the jwt claim with the name from the pre-join screen - local occupant_nick = stanza:get_child('nick', NICK_NS); - if occupant_nick then - local pre_join_screen_name = occupant_nick:get_text() - if pre_join_screen_name and session.jitsi_meet_context_user then - session.jitsi_meet_context_user["name"] = pre_join_screen_name; - end - end - payload = type(session.jitsi_meet_context_user) == "table" and util.shallow_copy(session.jitsi_meet_context_user) or {} - if session.jitsi_meet_context_group then - payload.group = session.jitsi_meet_context_group; + if is_healthcheck_room(room.jid) or is_admin(occupant.bare_jid) then + return; + end + + if DEBUG then + module:log("debug", "Will send participant event %s for room %s is_breakout (%s, main room jid:%s)", + occupant.jid, room.jid, is_breakout, main_room.jid); + end + local meeting_fqn, customer_id = util.get_fqn_and_customer_id(main_room); + local _, _, nick_resource = split_jid(occupant.nick); + local session = event.origin; + local payload = {}; + + if session and session.auth_token then + -- replace user name from the jwt claim with the name from the pre-join screen + local occupant_nick = stanza:get_child('nick', NICK_NS); + if occupant_nick then + local pre_join_screen_name = occupant_nick:get_text() + if pre_join_screen_name and session.jitsi_meet_context_user then + session.jitsi_meet_context_user["name"] = pre_join_screen_name; end end - payload.participantJid = occupant.bare_jid; - payload.participantId = nick_resource; - payload.participantFullJid = occupant.jid; - payload.conference = internal_room_jid_match_rewrite(room.jid); - - if (is_lobby) then - -- manually extract participantId from jid, because the nick resource is different for lobby - payload.participantId = occupant.jid:match("^(.-)-"); + payload = type(session.jitsi_meet_context_user) == "table" and util.shallow_copy(session.jitsi_meet_context_user) or {} + if session.jitsi_meet_context_group then + payload.group = session.jitsi_meet_context_group; end - -- dial check - if dial_participants[occupant.jid] ~= nil then - if DEBUG then module:log("debug", "dial participant %s leave room %s", occupant.jid, room.jid); end - if dial_participants[occupant.jid] == "in" then - final_event_type = DIAL_IN_ENDED; - elseif dial_participants[occupant.jid] == "out" then - final_event_type = DIAL_OUT_ENDED - end + end + payload.participantJid = occupant.bare_jid; + payload.participantId = nick_resource; + payload.participantFullJid = occupant.jid; + payload.conference = internal_room_jid_match_rewrite(room.jid); + + if (is_lobby) then + -- manually extract participantId from jid, because the nick resource is different for lobby + payload.participantId = occupant.jid:match("^(.-)-"); + end + -- dial check + if dial_participants[occupant.jid] ~= nil then + if DEBUG then module:log("debug", "dial participant %s leave room %s", occupant.jid, room.jid); end + if dial_participants[occupant.jid] == "in" then + final_event_type = DIAL_IN_ENDED; + elseif dial_participants[occupant.jid] == "out" then + final_event_type = DIAL_OUT_ENDED end - local initiator; - if stanza then - initiator = stanza:get_child('initiator', 'http://jitsi.org/protocol/jigasi'); - if initiator and stanza.attr.type ~= 'unavailable' then - if DEBUG then module:log("debug", "dial participant %s joined room %s", occupant.jid, room.jid); end - local nick = stanza:get_child('nick', NICK_NS); - if nick then - payload.nick = nick:get_text(); - end - local call_direction; + end + + if stanza then + if oss_util.is_sip_jigasi(stanza) and stanza.attr.type ~= 'unavailable' then + if DEBUG then module:log("debug", "dial participant %s joined room %s", occupant.jid, room.jid); end + local nick = stanza:get_child('nick', NICK_NS); + if nick then + payload.nick = nick:get_text(); + end + + local call_direction; + local initiator = stanza:get_child('initiator', 'http://jitsi.org/protocol/jigasi'); + if initiator then initiator:maptags(function(tag) if tag.name == "header" and tag.attr.name == JIGASI_CALL_DIRECTION_ATTR_NAME then call_direction = tag.attr.value; end return tag; end); - payload.direction = call_direction; - room._data.dial_participants = room._data.dial_participants or {} - room._data.dial_participants[occupant.jid] = call_direction; - if call_direction == "in" then - final_event_type = DIAL_IN_STARTED - elseif call_direction == "out" then - final_event_type = DIAL_OUT_STARTED - end end - end - -- transcriber check - if stanza and oss_util.starts_with_one_of(occupant.jid, TRANSCRIBER_PREFIXES) then - local presence_type = stanza.attr.type; - if not presence_type then - if DEBUG then module:log("debug", "Transcriber %s join the room %s", occupant.jid, room.jid); end - final_event_type = TRANSCRIPTION_STARTED - elseif presence_type == 'unavailable' then - if DEBUG then module:log("debug", "Transcriber %s leave the room %s", occupant.jid, room.jid); end - final_event_type = TRANSCRIPTION_ENDED + payload.direction = call_direction; + room._data.dial_participants = room._data.dial_participants or {} + room._data.dial_participants[occupant.jid] = call_direction; + if call_direction == "in" then + final_event_type = DIAL_IN_STARTED + elseif call_direction == "out" then + final_event_type = DIAL_OUT_STARTED end end + end - if not is_lobby then - payload.isBreakout = is_breakout; - payload.breakoutRoomId = breakout_room_id; + -- transcriber check + if stanza and oss_util.starts_with_one_of(occupant.jid, TRANSCRIBER_PREFIXES) then + local presence_type = stanza.attr.type; + if not presence_type then + if DEBUG then module:log("debug", "Transcriber %s join the room %s", occupant.jid, room.jid); end + final_event_type = TRANSCRIPTION_STARTED + elseif presence_type == 'unavailable' then + if DEBUG then module:log("debug", "Transcriber %s leave the room %s", occupant.jid, room.jid); end + final_event_type = TRANSCRIPTION_ENDED end + end - local participant_access_event = { - ["idempotencyKey"] = uuid_gen(), - ["sessionId"] = main_room._data.meetingId, - ["created"] = util.round(socket.gettime() * 1000), - ["meetingFqn"] = meeting_fqn, - ["eventType"] = final_event_type, - ["data"] = payload - } - if is_vpaas(main_room) then - participant_access_event["customerId"] = customer_id + if not is_lobby then + payload.isBreakout = is_breakout; + payload.breakoutRoomId = breakout_room_id; + end + + local participant_access_event = { + ["idempotencyKey"] = uuid_gen(), + ["sessionId"] = main_room._data.meetingId, + ["created"] = util.round(socket.gettime() * 1000), + ["meetingFqn"] = meeting_fqn, + ["eventType"] = final_event_type, + ["data"] = payload + } + if is_vpaas(main_room) then + participant_access_event["customerId"] = customer_id + else + if session and session.jitsi_meet_context_group then + participant_access_event["customerId"] = session.jitsi_meet_context_group; else - if session and session.jitsi_meet_context_group then - participant_access_event["customerId"] = session.jitsi_meet_context_group; - else - participant_access_event["customerId"] = customer_id; - end + participant_access_event["customerId"] = customer_id; end + end - -- live stream/recording - if oss_util.starts_with_one_of(occupant.jid, RECORDER_PREFIXES) then - local recorderType = event.room._data.recorderType; - if final_event_type == PARTICIPANT_JOINED then - if DEBUG then module:log("debug", "Recorder %s joined", event.occupant.jid); end - if recorderType == 'recorder' then - participant_access_event["eventType"] = RECORDING_STARTED - elseif recorderType == 'live_stream' then - participant_access_event["eventType"] = LIVE_STREAM_STARTED - end - elseif final_event_type == PARTICIPANT_LEFT then - if DEBUG then module:log("debug", "Recorder %s left", event.occupant.jid); end - if recorderType == 'recorder' then - participant_access_event["eventType"] = RECORDING_ENDED - elseif recorderType == 'live_stream' then - participant_access_event["eventType"] = LIVE_STREAM_ENDED - end + -- live stream/recording + if oss_util.is_jibri(occupant) then + local recorderType = event.room._data.recorderType; + if final_event_type == PARTICIPANT_JOINED then + if DEBUG then module:log("debug", "Recorder %s joined", event.occupant.jid); end + if recorderType == 'recorder' then + participant_access_event["eventType"] = RECORDING_STARTED + elseif recorderType == 'live_stream' then + participant_access_event["eventType"] = LIVE_STREAM_STARTED + end + elseif final_event_type == PARTICIPANT_LEFT then + if DEBUG then module:log("debug", "Recorder %s left", event.occupant.jid); end + if recorderType == 'recorder' then + participant_access_event["eventType"] = RECORDING_ENDED + elseif recorderType == 'live_stream' then + participant_access_event["eventType"] = LIVE_STREAM_ENDED end - local rec_payload = {} - rec_payload.conference = internal_room_jid_match_rewrite(room.jid); - participant_access_event["data"] = rec_payload end + local rec_payload = {} + rec_payload.conference = internal_room_jid_match_rewrite(room.jid); + participant_access_event["data"] = rec_payload + end - -- sip call - -- sip participant will send at least 2 presence events on each join - -- one for the first join, containing basic info - -- another one shortly after, containing additional info, such as the participant's sip_address - -- multiple other presence updates can be sent apart from the 2 mandatory presences - -- we process only the first presence containing the sip address - if event_type == PARTICIPANT_JOINED and oss_util.is_sip_jibri_join(stanza) then - room._data.sip_participants_events = room._data.sip_participants_events or {} - -- we must send sip address on the webhook - local sip_address = stanza:get_child_text('sip_address'); - local is_new_sip_participant = (not room._data.sip_participants_events[occupant.jid]) or (room._data.sip_participants_events[occupant.jid] == SIP_PARTICIPANT_NOT_JOINED_YET); - - if sip_address and is_new_sip_participant then - local sip_jibri_prefix = util.get_sip_jibri_prefix(stanza); - - if oss_util.starts_with_one_of(sip_jibri_prefix, util.INBOUND_SIP_JIBRI_PREFIXES) then - participant_access_event["eventType"] = SIP_CALL_IN_STARTED; - final_event_type = SIP_CALL_IN_STARTED; - elseif oss_util.starts_with_one_of(sip_jibri_prefix, util.OUTBOUND_SIP_JIBRI_PREFIXES) then - participant_access_event["eventType"] = SIP_CALL_OUT_STARTED; - final_event_type = SIP_CALL_OUT_STARTED; - end + -- sip call + -- sip participant will send at least 2 presence events on each join + -- one for the first join, containing basic info + -- another one shortly after, containing additional info, such as the participant's sip_address + -- multiple other presence updates can be sent apart from the 2 mandatory presences + -- we process only the first presence containing the sip address + if event_type == PARTICIPANT_JOINED and oss_util.is_sip_jibri_join(stanza) then + room._data.sip_participants_events = room._data.sip_participants_events or {} + -- we must send sip address on the webhook + local sip_address = stanza:get_child_text('sip_address'); + local is_new_sip_participant = (not room._data.sip_participants_events[occupant.jid]) or (room._data.sip_participants_events[occupant.jid] == SIP_PARTICIPANT_NOT_JOINED_YET); - participant_access_event["data"]["sipAddress"] = sip_address - local nick = stanza:get_child('nick', NICK_NS); - if nick then - participant_access_event["data"]["nick"] = nick:get_text(); - end + if sip_address and is_new_sip_participant then + local sip_jibri_prefix = util.get_sip_jibri_prefix(stanza); - room._data.sip_participants_events[occupant.jid] = participant_access_event["eventType"]; - module:log("info", "%s: sip participant %s joined the room %s", participant_access_event["eventType"], occupant.jid, room.jid) - elseif sip_address and not is_new_sip_participant then - if DEBUG then - module:log("debug", "Ignoring the sip participant %s presence update for room %s", - occupant.jid, room.jid); - end - final_event_type = SIP_PARTICIPANT_ALREADY_JOINED; - else - -- no sip_address - if DEBUG then - module:log("debug", "Ignoring the sip participant %s presence for room %s, as it has no sip address", - occupant.jid, room.jid); - end - final_event_type = SIP_PARTICIPANT_NOT_JOINED_YET; - room._data.sip_participants_events[occupant.jid] = SIP_PARTICIPANT_NOT_JOINED_YET; - end - elseif event_type == PARTICIPANT_LEFT and room._data.sip_participants_events and room._data.sip_participants_events[occupant.jid] then - if room._data.sip_participants_events[occupant.jid] == SIP_CALL_IN_STARTED then - participant_access_event["eventType"] = SIP_CALL_IN_ENDED; - final_event_type = SIP_CALL_IN_ENDED; - elseif room._data.sip_participants_events[occupant.jid] == SIP_CALL_OUT_STARTED then - participant_access_event["eventType"] = SIP_CALL_OUT_ENDED; - final_event_type = SIP_CALL_OUT_ENDED; - elseif room._data.sip_participants_events[occupant.jid] == SIP_PARTICIPANT_NOT_JOINED_YET then - final_event_type = SIP_PARTICIPANT_NOT_JOINED_YET; + if oss_util.starts_with_one_of(sip_jibri_prefix, util.INBOUND_SIP_JIBRI_PREFIXES) then + participant_access_event["eventType"] = SIP_CALL_IN_STARTED; + final_event_type = SIP_CALL_IN_STARTED; + elseif oss_util.starts_with_one_of(sip_jibri_prefix, util.OUTBOUND_SIP_JIBRI_PREFIXES) then + participant_access_event["eventType"] = SIP_CALL_OUT_STARTED; + final_event_type = SIP_CALL_OUT_STARTED; end - room._data.sip_participants_events[occupant.jid] = nil - if not (final_event_type == SIP_PARTICIPANT_NOT_JOINED_YET) then - module:log("info", "%s: sip participant %s left the room %s", participant_access_event["eventType"], occupant.jid, room.jid) + + participant_access_event["data"]["sipAddress"] = sip_address + local nick = stanza:get_child('nick', NICK_NS); + if nick then + participant_access_event["data"]["nick"] = nick:get_text(); end - end - if final_event_type == SIP_PARTICIPANT_NOT_JOINED_YET or final_event_type == SIP_PARTICIPANT_ALREADY_JOINED then + room._data.sip_participants_events[occupant.jid] = participant_access_event["eventType"]; + module:log("info", "%s: sip participant %s joined the room %s", participant_access_event["eventType"], occupant.jid, room.jid) + elseif sip_address and not is_new_sip_participant then if DEBUG then - module:log("debug", "Ignoring sip event %s, the event either does not contain the sip_address, or is a presence update which we don't send as webhook: %s", - event_type, stanza); + module:log("debug", "Ignoring the sip participant %s presence update for room %s", + occupant.jid, room.jid); end - return + final_event_type = SIP_PARTICIPANT_ALREADY_JOINED; + else + -- no sip_address + if DEBUG then + module:log("debug", "Ignoring the sip participant %s presence for room %s, as it has no sip address", + occupant.jid, room.jid); + end + final_event_type = SIP_PARTICIPANT_NOT_JOINED_YET; + room._data.sip_participants_events[occupant.jid] = SIP_PARTICIPANT_NOT_JOINED_YET; end + elseif event_type == PARTICIPANT_LEFT and room._data.sip_participants_events and room._data.sip_participants_events[occupant.jid] then + if room._data.sip_participants_events[occupant.jid] == SIP_CALL_IN_STARTED then + participant_access_event["eventType"] = SIP_CALL_IN_ENDED; + final_event_type = SIP_CALL_IN_ENDED; + elseif room._data.sip_participants_events[occupant.jid] == SIP_CALL_OUT_STARTED then + participant_access_event["eventType"] = SIP_CALL_OUT_ENDED; + final_event_type = SIP_CALL_OUT_ENDED; + elseif room._data.sip_participants_events[occupant.jid] == SIP_PARTICIPANT_NOT_JOINED_YET then + final_event_type = SIP_PARTICIPANT_NOT_JOINED_YET; + end + room._data.sip_participants_events[occupant.jid] = nil + if not (final_event_type == SIP_PARTICIPANT_NOT_JOINED_YET) then + module:log("info", "%s: sip participant %s left the room %s", participant_access_event["eventType"], occupant.jid, room.jid) + end + end - if not util.is_blacklisted(occupant) and is_vpaas(main_room) - and (final_event_type == PARTICIPANT_JOINED or final_event_type == PARTICIPANT_LEFT) - and event.origin and not event.origin.auth_token - and not event.origin.vpaas_guest_access then - local event = 'join'; - if final_event_type == PARTICIPANT_LEFT then - event = 'leave'; - end - module:log("warn", "Occupant %s tried to %s a jaas room %s without a token", occupant.jid, event, room.jid); - -- do not send join/left/usage events for JaaS participants without a jwt. - return; + if final_event_type == SIP_PARTICIPANT_NOT_JOINED_YET or final_event_type == SIP_PARTICIPANT_ALREADY_JOINED then + if DEBUG then + module:log("debug", "Ignoring sip event %s, the event either does not contain the sip_address, or is a presence update which we don't send as webhook: %s", + event_type, stanza); end + return + end - -- lobby events - if is_lobby then - if final_event_type == PARTICIPANT_JOINED then - if DEBUG then module:log("debug", "Occupant %s joined lobby room %s", occupant.jid, room.jid); end - if main_room:get_affiliation(occupant.bare_jid) == 'owner' or occupant.role == "moderator" then - moderator_occupants_in_lobby[occupant.bare_jid] = 'owner'; - return; - end - participant_access_event["eventType"] = PARTICIPANT_JOINED_LOBBY; - elseif final_event_type == PARTICIPANT_LEFT then - if DEBUG then module:log("debug", "Occupant %s left lobby room %s", occupant.jid, room.jid); end - if moderator_occupants_in_lobby[occupant.bare_jid] ~= nil then - -- clear from list - moderator_occupants_in_lobby[occupant.bare_jid] = nil; - return; - end - participant_access_event["eventType"] = PARTICIPANT_LEFT_LOBBY; - end + if not oss_util.starts_with_one_of(occupant.jid, TRANSCRIBER_PREFIXES) + and not oss_util.is_jibri(occupant) + and not oss_util.is_sip_jibri_join(stanza) + and not oss_util.is_sip_jigasi(stanza) + and is_vpaas(main_room) + and (final_event_type == PARTICIPANT_JOINED or final_event_type == PARTICIPANT_LEFT) + and event.origin and not event.origin.auth_token + and not event.origin.vpaas_guest_access then + local event = 'join'; + if final_event_type == PARTICIPANT_LEFT then + event = 'leave'; end + module:log("warn", "Occupant %s tried to %s a jaas room %s without a token", occupant.jid, event, room.jid); + -- do not send join/left/usage events for JaaS participants without a jwt. + return; + end - -- add reason for participant left - if final_event_type == PARTICIPANT_LEFT or final_event_type == DIAL_OUT_ENDED or final_event_type == SIP_CALL_IN_ENDED or final_event_type == SIP_CALL_OUT_ENDED then - -- check if the participant switch the main for the breakout room or vice versa - local status_tag = stanza and stanza:get_child('status') or nil; - if status_tag and status_tag:get_text() == 'switch_room' then - payload.disconnectReason = 'switch_room' - elseif status_tag and status_tag:get_text() == 'unrecoverable_error' then - payload.disconnectReason = 'unrecoverable_error' - elseif KICKED_PARTICIPANTS_NICK[nick_resource] then - payload.disconnectReason = 'kicked' - KICKED_PARTICIPANTS_NICK[nick_resource] = nil - elseif DISCONNECTED_PARTICIPANTS_JID[occupant.jid] then - payload.disconnectReason = 'unknown' - DISCONNECTED_PARTICIPANTS_JID[occupant.jid] = nil - else - payload.disconnectReason = 'left' + -- lobby events + if is_lobby then + if final_event_type == PARTICIPANT_JOINED then + if DEBUG then module:log("debug", "Occupant %s joined lobby room %s", occupant.jid, room.jid); end + if main_room:get_affiliation(occupant.bare_jid) == 'owner' or occupant.role == "moderator" then + moderator_occupants_in_lobby[occupant.bare_jid] = 'owner'; + return; + end + participant_access_event["eventType"] = PARTICIPANT_JOINED_LOBBY; + elseif final_event_type == PARTICIPANT_LEFT then + if DEBUG then module:log("debug", "Occupant %s left lobby room %s", occupant.jid, room.jid); end + if moderator_occupants_in_lobby[occupant.bare_jid] ~= nil then + -- clear from list + moderator_occupants_in_lobby[occupant.bare_jid] = nil; + return; end + participant_access_event["eventType"] = PARTICIPANT_LEFT_LOBBY; end + end - -- in case of PARTICIPANT_LEFT or PARTICIPANT_JOINED events add flip field in data payload - decorate_payload_with_flip(payload, occupant.nick, main_room, final_event_type); + -- add reason for participant left + if final_event_type == PARTICIPANT_LEFT or final_event_type == DIAL_OUT_ENDED or final_event_type == SIP_CALL_IN_ENDED or final_event_type == SIP_CALL_OUT_ENDED then + -- check if the participant switch the main for the breakout room or vice versa + local status_tag = stanza and stanza:get_child('status') or nil; + if status_tag and status_tag:get_text() == 'switch_room' then + payload.disconnectReason = 'switch_room' + elseif status_tag and status_tag:get_text() == 'unrecoverable_error' then + payload.disconnectReason = 'unrecoverable_error' + elseif KICKED_PARTICIPANTS_NICK[nick_resource] then + payload.disconnectReason = 'kicked' + KICKED_PARTICIPANTS_NICK[nick_resource] = nil + elseif DISCONNECTED_PARTICIPANTS_JID[occupant.jid] then + payload.disconnectReason = 'unknown' + DISCONNECTED_PARTICIPANTS_JID[occupant.jid] = nil + else + payload.disconnectReason = 'left' + end + end - if DEBUG then module:log("debug", "Participant event %s", inspect(participant_access_event)); end + -- in case of PARTICIPANT_LEFT or PARTICIPANT_JOINED events add flip field in data payload + decorate_payload_with_flip(payload, occupant.nick, main_room, final_event_type); - event_count(); - http.request(EGRESS_URL, { - headers = util.http_headers_no_auth, - method = "POST", - body = json.encode(participant_access_event); - }, cb); + if DEBUG then module:log("debug", "Participant event %s", inspect(participant_access_event)); end - -- send MAU usage for normal participants and dial calls only - -- live stream/recording/sip calls are billed based on duration and not MAU - if not util.is_blacklisted(occupant) and is_vpaas(main_room) and event_type == PARTICIPANT_JOINED then - local is_sip_jibri_event = final_event_type == SIP_CALL_IN_STARTED or final_event_type == SIP_CALL_OUT_STARTED or final_event_type == SIP_CALL_IN_ENDED or final_event_type == SIP_CALL_OUT_ENDED - if not is_breakout then - if not is_sip_jibri_event then - local participants = room._data.participants_jid or {}; - table.insert(participants, occupant.bare_jid); - room._data.participants_jid = participants; - local current_usage_item = get_current_event_usage(session, nick_resource, customer_id, final_event_type, payload.nick, payload.direction); - handle_usage_update(main_room, meeting_fqn, current_usage_item, breakout_room_id, is_sip_jibri_event); - else - handle_usage_update(main_room, meeting_fqn, nil, breakout_room_id, is_sip_jibri_event) - end + event_count(); + http.request(EGRESS_URL, { + headers = util.http_headers_no_auth, + method = "POST", + body = json.encode(participant_access_event); + }, cb); + + -- send MAU usage for normal participants and dial calls only + -- live stream/recording/sip calls are billed based on duration and not MAU + if not oss_util.starts_with_one_of(occupant.jid, TRANSCRIBER_PREFIXES) + and not oss_util.is_jibri(occupant) + and not oss_util.is_sip_jibri_join(stanza) + and is_vpaas(main_room) and event_type == PARTICIPANT_JOINED then + local is_sip_jibri_event = final_event_type == SIP_CALL_IN_STARTED or final_event_type == SIP_CALL_OUT_STARTED or final_event_type == SIP_CALL_IN_ENDED or final_event_type == SIP_CALL_OUT_ENDED + if not is_breakout then + if not is_sip_jibri_event then + local participants = room._data.participants_jid or {}; + table.insert(participants, occupant.bare_jid); + room._data.participants_jid = participants; + local current_usage_item = get_current_event_usage(session, nick_resource, customer_id, final_event_type, payload.nick, payload.direction); + handle_usage_update(main_room, meeting_fqn, current_usage_item, breakout_room_id, is_sip_jibri_event); + else + handle_usage_update(main_room, meeting_fqn, nil, breakout_room_id, is_sip_jibri_event) end end end diff --git a/ansible/roles/prosody/files/util.internal.lib.lua b/ansible/roles/prosody/files/util.internal.lib.lua index 4f1c89cf..366132c3 100644 --- a/ansible/roles/prosody/files/util.internal.lib.lua +++ b/ansible/roles/prosody/files/util.internal.lib.lua @@ -3,6 +3,8 @@ local jid = require "util.jid"; local json = require "cjson"; local inspect = require('inspect'); +local oss_util = module:require "util"; + local Util = {} -- required parameter for custom muc component prefix, @@ -15,9 +17,6 @@ if not muc_domain_base then muc_domain_base = "" end -local blacklist_prefix = module:get_option_array("muc_events_blacklist_prefixes", { 'focus@auth.', 'recorder@recorder.','jibria@recorder', 'jibrib@recorder', 'jvb@auth.', 'jibri@auth.','jibria@auth.','jibrib@auth.', 'transcriber@recorder.','transcribera@recorder.','transcriberb@recorder.', 'jigasi@auth.', 'jigasia@auth.','jigasib@auth.' }); -local blacklist_domain_prefix = module:get_option_array("muc_events_blacklist_domain_prefixes", {}); - -- The "real" MUC domain that we are proxying to local muc_domain = module:get_option_string("muc_mapper_domain", muc_domain_prefix .. "." .. muc_domain_base); @@ -138,36 +137,6 @@ function Util.round(num, numDecimalPlaces) return math.floor(num * mult + 0.5) / mult end --- Check if the occupant is a regular user --- @param occupant from event or occupant jid -function Util.is_blacklisted(occupant) - local occupant_jid; - if not occupant then - return false; - end - - if not occupant.bare_jid then - occupant_jid = occupant - else - occupant_jid = occupant.bare_jid; - end - - for _, prefix in ipairs(blacklist_prefix) do - if string.sub(occupant_jid, 1, string.len(prefix)) == prefix then - module:log("debug", "Occupant %s is blacklisted ", occupant_jid); - return true; - end - end - local occupant_domain = jid.host(occupant_jid); - for _, prefix in ipairs(blacklist_domain_prefix) do - if string.sub(occupant_domain, 1, string.len(prefix)) == prefix then - module:log("debug", "Occupant %s is blacklisted by domain", occupant_jid); - return true; - end - end - return false; -end - function Util.get_sip_jibri_prefix(stanza) if not stanza then return nil;