mirror of
https://git.lapiole.org/dani/ansible-roles.git
synced 2025-07-18 10:36:51 +02:00
Update to 2025-06-16 16:00
This commit is contained in:
parent
ffe9caded5
commit
eb0714be6f
@ -1,8 +1,8 @@
|
|||||||
---
|
---
|
||||||
|
|
||||||
# Version of consul to deploy
|
# Version of consul to deploy
|
||||||
consul_version: 1.21.0
|
consul_version: 1.21.1
|
||||||
# URL from where the consul archive will be downloaded
|
# URL from where the consul archive will be downloaded
|
||||||
consul_archive_url: https://releases.hashicorp.com/consul/{{ consul_version }}/consul_{{ consul_version }}_linux_amd64.zip
|
consul_archive_url: https://releases.hashicorp.com/consul/{{ consul_version }}/consul_{{ consul_version }}_linux_amd64.zip
|
||||||
# Expected sha256 of the archive
|
# Expected sha256 of the archive
|
||||||
consul_archive_sha256: e916e30904eedfa7ee2e2a378b5e8a9a374f2f351e645aa4c0a03adc15dabaec
|
consul_archive_sha256: cf5b8d429c67d4e3c86e2f52eb3245ee00119a9a389f2af36a77b16b1e1eb27c
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
---
|
---
|
||||||
|
|
||||||
# Version of consul-template to install
|
# Version of consul-template to install
|
||||||
consul_tpl_version: 0.40.0
|
consul_tpl_version: 0.41.0
|
||||||
# URL of the archive
|
# URL of the archive
|
||||||
consul_tpl_archive_url: https://releases.hashicorp.com/consul-template/{{ consul_tpl_version }}/consul-template_{{ consul_tpl_version }}_linux_amd64.zip
|
consul_tpl_archive_url: https://releases.hashicorp.com/consul-template/{{ consul_tpl_version }}/consul-template_{{ consul_tpl_version }}_linux_amd64.zip
|
||||||
# Expected sha256 of the archive
|
# Expected sha256 of the archive
|
||||||
consul_tpl_archive_sha256: f73cb36988b9aaccb0ac918df26c854ccd199e60c0df011357405672f3d934bc
|
consul_tpl_archive_sha256: 64e732cdd75a778ea6a5e16b32792a1effc88963d37e73f0088a115ea790938f
|
||||||
|
|
||||||
# Root dir where consul-template will be installed
|
# Root dir where consul-template will be installed
|
||||||
consul_tpl_root_dir: /opt/consul_template
|
consul_tpl_root_dir: /opt/consul_template
|
||||||
|
@ -7,6 +7,11 @@ docker_base_conf:
|
|||||||
data-root: /opt/docker
|
data-root: /opt/docker
|
||||||
log-driver: journald
|
log-driver: journald
|
||||||
storage-driver: overlay2
|
storage-driver: overlay2
|
||||||
|
default-ulimits:
|
||||||
|
nofile:
|
||||||
|
Hard: 1048576
|
||||||
|
Soft: 1048576
|
||||||
|
Name: nofile
|
||||||
docker_extra_conf: {}
|
docker_extra_conf: {}
|
||||||
# docker_extra_conf:
|
# docker_extra_conf:
|
||||||
# log-opts:
|
# log-opts:
|
||||||
|
@ -9,16 +9,16 @@ jitsi_user: jitsi
|
|||||||
jitsi_web_src_ip:
|
jitsi_web_src_ip:
|
||||||
- 0.0.0.0/0
|
- 0.0.0.0/0
|
||||||
|
|
||||||
jitsi_version: 10133
|
jitsi_version: 10314
|
||||||
|
|
||||||
jitsi_jicofo_archive_url: https://github.com/jitsi/jicofo/archive/refs/tags/stable/jitsi-meet_{{ jitsi_version }}.tar.gz
|
jitsi_jicofo_archive_url: https://github.com/jitsi/jicofo/archive/refs/tags/stable/jitsi-meet_{{ jitsi_version }}.tar.gz
|
||||||
jitsi_jicofo_archive_sha256: a233b30fbbb41c30cdef0bbfbf971d7dcb3b0ce96a54f16a4bf9e1b7633f31fc
|
jitsi_jicofo_archive_sha256: 0e081653d525462bfa1358ff6a25b091636792c4ac4a4fbf0b6235951d7cc4ac
|
||||||
|
|
||||||
# Jigasi has no release, nor tags, so use master
|
# Jigasi has no release, nor tags, so use master
|
||||||
jitsi_jigasi_archive_url: https://github.com/jitsi/jigasi/archive/refs/heads/master.tar.gz
|
jitsi_jigasi_archive_url: https://github.com/jitsi/jigasi/archive/refs/heads/master.tar.gz
|
||||||
|
|
||||||
jitsi_meet_archive_url: https://github.com/jitsi/jitsi-meet/archive/refs/tags/stable/jitsi-meet_{{ jitsi_version }}.tar.gz
|
jitsi_meet_archive_url: https://github.com/jitsi/jitsi-meet/archive/refs/tags/stable/jitsi-meet_{{ jitsi_version }}.tar.gz
|
||||||
jitsi_meet_archive_sha256: c1ed6ff9546fe681ac3996b49bee22df420350f406468408acbe58491d95c0d9
|
jitsi_meet_archive_sha256: 04770928b232fb794206083f9f4cdfe24112ce8dd57e84c253380afb39ebc5d3
|
||||||
|
|
||||||
jitsi_excalidraw_version: 2025.2.2
|
jitsi_excalidraw_version: 2025.2.2
|
||||||
jitsi_excalidraw_archive_url: https://github.com/jitsi/excalidraw-backend/archive/refs/tags/{{ jitsi_excalidraw_version }}.tar.gz
|
jitsi_excalidraw_archive_url: https://github.com/jitsi/excalidraw-backend/archive/refs/tags/{{ jitsi_excalidraw_version }}.tar.gz
|
||||||
|
@ -226,7 +226,11 @@ function M.verify(token, expectedAlgo, key, acceptedIssuers, acceptedAudiences)
|
|||||||
|
|
||||||
|
|
||||||
if body.exp and os.time() >= body.exp then
|
if body.exp and os.time() >= body.exp then
|
||||||
return nil, "Not acceptable by exp ("..tostring(os.time()-body.exp)..")"
|
local extra_msg = '';
|
||||||
|
if body.iat then
|
||||||
|
extra_msg = ", valid for:"..tostring(body.exp-body.iat).." sec";
|
||||||
|
end
|
||||||
|
return nil, "Not acceptable by exp ("..tostring(os.time()-body.exp).." sec since expired"..extra_msg..")"
|
||||||
end
|
end
|
||||||
|
|
||||||
if body.nbf and os.time() < body.nbf then
|
if body.nbf and os.time() < body.nbf then
|
||||||
|
@ -48,13 +48,14 @@ function init_session(event)
|
|||||||
-- After validating auth_token will be cleaned in case of error and few
|
-- After validating auth_token will be cleaned in case of error and few
|
||||||
-- other fields will be extracted from the token and set in the session
|
-- other fields will be extracted from the token and set in the session
|
||||||
|
|
||||||
if query and params.token then
|
if params and params.token then
|
||||||
token = params.token;
|
token = params.token;
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- in either case set auth_token in the session
|
-- in either case set auth_token in the session
|
||||||
session.auth_token = token;
|
session.auth_token = token;
|
||||||
|
session.user_agent_header = request.headers['user_agent'];
|
||||||
end
|
end
|
||||||
|
|
||||||
module:hook_global("bosh-session", init_session);
|
module:hook_global("bosh-session", init_session);
|
||||||
@ -101,8 +102,9 @@ function provider.get_sasl_handler(session)
|
|||||||
local res, error, reason = token_util:process_and_verify_token(session);
|
local res, error, reason = token_util:process_and_verify_token(session);
|
||||||
if res == false then
|
if res == false then
|
||||||
module:log("warn",
|
module:log("warn",
|
||||||
"Error verifying token err:%s, reason:%s tenant:%s room:%s",
|
"Error verifying token err:%s, reason:%s tenant:%s room:%s user_agent:%s",
|
||||||
error, reason, session.jitsi_web_query_prefix, session.jitsi_web_query_room);
|
error, reason, session.jitsi_web_query_prefix, session.jitsi_web_query_room,
|
||||||
|
session.user_agent_header);
|
||||||
session.auth_token = nil;
|
session.auth_token = nil;
|
||||||
measure_verify_fail(1);
|
measure_verify_fail(1);
|
||||||
return res, error, reason;
|
return res, error, reason;
|
||||||
|
@ -4,6 +4,7 @@ local is_healthcheck_room = util.is_healthcheck_room;
|
|||||||
local internal_room_jid_match_rewrite = util.internal_room_jid_match_rewrite;
|
local internal_room_jid_match_rewrite = util.internal_room_jid_match_rewrite;
|
||||||
local room_jid_match_rewrite = util.room_jid_match_rewrite;
|
local room_jid_match_rewrite = util.room_jid_match_rewrite;
|
||||||
local process_host_module = util.process_host_module;
|
local process_host_module = util.process_host_module;
|
||||||
|
local table_shallow_copy = util.table_shallow_copy;
|
||||||
local array = require "util.array";
|
local array = require "util.array";
|
||||||
local json = require 'cjson.safe';
|
local json = require 'cjson.safe';
|
||||||
local st = require 'util.stanza';
|
local st = require 'util.stanza';
|
||||||
@ -47,7 +48,7 @@ function notify_occupants_enable(jid, enable, room, actorJid, mediaType)
|
|||||||
body_json.type = 'av_moderation';
|
body_json.type = 'av_moderation';
|
||||||
body_json.enabled = enable;
|
body_json.enabled = enable;
|
||||||
body_json.room = internal_room_jid_match_rewrite(room.jid);
|
body_json.room = internal_room_jid_match_rewrite(room.jid);
|
||||||
body_json.actor = actorJid;
|
body_json.actor = internal_room_jid_match_rewrite(actorJid);
|
||||||
body_json.mediaType = mediaType;
|
body_json.mediaType = mediaType;
|
||||||
local body_json_str, error = json.encode(body_json);
|
local body_json_str, error = json.encode(body_json);
|
||||||
|
|
||||||
@ -75,11 +76,20 @@ function notify_whitelist_change(jid, moderators, room, mediaType, removed)
|
|||||||
local body_json = {};
|
local body_json = {};
|
||||||
body_json.type = 'av_moderation';
|
body_json.type = 'av_moderation';
|
||||||
body_json.room = internal_room_jid_match_rewrite(room.jid);
|
body_json.room = internal_room_jid_match_rewrite(room.jid);
|
||||||
body_json.whitelists = room.av_moderation;
|
-- we will be modifying it, so we need a copy
|
||||||
|
body_json.whitelists = table_shallow_copy(room.av_moderation);
|
||||||
if removed then
|
if removed then
|
||||||
body_json.removed = true;
|
body_json.removed = true;
|
||||||
end
|
end
|
||||||
body_json.mediaType = mediaType;
|
body_json.mediaType = mediaType;
|
||||||
|
|
||||||
|
-- sanitize, make sure we don't have an empty array as it will encode it as {} not as []
|
||||||
|
for _,mediaType in pairs({'audio', 'video'}) do
|
||||||
|
if body_json.whitelists[mediaType] and #body_json.whitelists[mediaType] == 0 then
|
||||||
|
body_json.whitelists[mediaType] = nil;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
local moderators_body_json_str, error = json.encode(body_json);
|
local moderators_body_json_str, error = json.encode(body_json);
|
||||||
|
|
||||||
if not moderators_body_json_str then
|
if not moderators_body_json_str then
|
||||||
@ -197,6 +207,20 @@ function on_message(event)
|
|||||||
room.av_moderation_actors = {};
|
room.av_moderation_actors = {};
|
||||||
end
|
end
|
||||||
room.av_moderation[mediaType] = array{};
|
room.av_moderation[mediaType] = array{};
|
||||||
|
|
||||||
|
-- We want to set startMuted policy in metadata, in case of new participants are joining to respect
|
||||||
|
-- it, that will be enforced by jicofo
|
||||||
|
local startMutedMetadata = room.jitsiMetadata.startMuted or {};
|
||||||
|
|
||||||
|
-- We want to keep the previous value of startMuted for this mediaType if av moderation is disabled
|
||||||
|
-- to be able to restore
|
||||||
|
local av_moderation_startMuted_restore = room.av_moderation_startMuted_restore or {};
|
||||||
|
av_moderation_startMuted_restore = startMutedMetadata[mediaType];
|
||||||
|
room.av_moderation_startMuted_restore = av_moderation_startMuted_restore;
|
||||||
|
|
||||||
|
startMutedMetadata[mediaType] = true;
|
||||||
|
room.jitsiMetadata.startMuted = startMutedMetadata;
|
||||||
|
|
||||||
room.av_moderation_actors[mediaType] = occupant.nick;
|
room.av_moderation_actors[mediaType] = occupant.nick;
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
@ -208,7 +232,11 @@ function on_message(event)
|
|||||||
room.av_moderation[mediaType] = nil;
|
room.av_moderation[mediaType] = nil;
|
||||||
room.av_moderation_actors[mediaType] = nil;
|
room.av_moderation_actors[mediaType] = nil;
|
||||||
|
|
||||||
-- clears room.av_moderation if empty
|
local startMutedMetadata = room.jitsiMetadata.startMuted or {};
|
||||||
|
local av_moderation_startMuted_restore = room.av_moderation_startMuted_restore or {};
|
||||||
|
startMutedMetadata[mediaType] = av_moderation_startMuted_restore[mediaType];
|
||||||
|
room.jitsiMetadata.startMuted = startMutedMetadata;
|
||||||
|
|
||||||
local is_empty = true;
|
local is_empty = true;
|
||||||
for key,_ in pairs(room.av_moderation) do
|
for key,_ in pairs(room.av_moderation) do
|
||||||
if room.av_moderation[key] then
|
if room.av_moderation[key] then
|
||||||
|
@ -1,5 +1,27 @@
|
|||||||
|
-- This module is enabled under the main virtual host
|
||||||
|
local cache = require 'util.cache';
|
||||||
|
local new_throttle = require 'util.throttle'.create;
|
||||||
local st = require "util.stanza";
|
local st = require "util.stanza";
|
||||||
local is_feature_allowed = module:require "util".is_feature_allowed;
|
local jid_bare = require "util.jid".bare;
|
||||||
|
local util = module:require 'util';
|
||||||
|
local is_feature_allowed = util.is_feature_allowed;
|
||||||
|
local get_ip = util.get_ip;
|
||||||
|
local get_room_from_jid = util.get_room_from_jid;
|
||||||
|
local room_jid_match_rewrite = util.room_jid_match_rewrite;
|
||||||
|
|
||||||
|
local limit_jibri_reach_ip_attempts;
|
||||||
|
local limit_jibri_reach_room_attempts;
|
||||||
|
local rates_per_ip;
|
||||||
|
local function load_config()
|
||||||
|
limit_jibri_reach_ip_attempts = module:get_option_number("max_number_ip_attempts_per_minute", 9);
|
||||||
|
limit_jibri_reach_room_attempts = module:get_option_number("max_number_room_attempts_per_minute", 3);
|
||||||
|
-- The size of the cache that saves state for IP addresses
|
||||||
|
cache_size = module:get_option_number("jibri_rate_limit_cache_size", 10000);
|
||||||
|
|
||||||
|
-- Maps an IP address to a util.throttle which keeps the rate of attempts to reach jibri events from that IP.
|
||||||
|
rates_per_ip = cache.new(cache_size);
|
||||||
|
end
|
||||||
|
load_config();
|
||||||
|
|
||||||
-- filters jibri iq in case of requested from jwt authenticated session that
|
-- filters jibri iq in case of requested from jwt authenticated session that
|
||||||
-- has features in the user context, but without feature for recording
|
-- has features in the user context, but without feature for recording
|
||||||
@ -10,15 +32,35 @@ module:hook("pre-iq/full", function(event)
|
|||||||
if jibri then
|
if jibri then
|
||||||
local session = event.origin;
|
local session = event.origin;
|
||||||
local token = session.auth_token;
|
local token = session.auth_token;
|
||||||
|
local room = get_room_from_jid(room_jid_match_rewrite(jid_bare(stanza.attr.to)));
|
||||||
|
local occupant = room:get_occupant_by_real_jid(stanza.attr.from);
|
||||||
|
local feature = jibri.attr.recording_mode == 'file' and 'recording' or 'livestreaming';
|
||||||
|
local is_allowed = is_feature_allowed(
|
||||||
|
feature,
|
||||||
|
session.jitsi_meet_context_features,
|
||||||
|
session.granted_jitsi_meet_context_features,
|
||||||
|
occupant.role == 'moderator');
|
||||||
|
|
||||||
if jibri.attr.action == 'start' then
|
if jibri.attr.action == 'start' or jibri.attr.action == 'stop' then
|
||||||
if token == nil
|
if not is_allowed then
|
||||||
or not is_feature_allowed(session.jitsi_meet_context_features,
|
module:log('info', 'Filtering jibri start recording, stanza:%s', tostring(stanza));
|
||||||
(jibri.attr.recording_mode == 'file' and 'recording' or 'livestreaming')
|
session.send(st.error_reply(stanza, 'auth', 'forbidden'));
|
||||||
) then
|
return true;
|
||||||
module:log("info",
|
end
|
||||||
"Filtering jibri start recording, stanza:%s", tostring(stanza));
|
|
||||||
session.send(st.error_reply(stanza, "auth", "forbidden"));
|
local ip = get_ip(session);
|
||||||
|
if not rates_per_ip:get(ip) then
|
||||||
|
rates_per_ip:set(ip, new_throttle(limit_jibri_reach_ip_attempts, 60));
|
||||||
|
end
|
||||||
|
|
||||||
|
if not room.jibri_throttle then
|
||||||
|
room.jibri_throttle = new_throttle(limit_jibri_reach_room_attempts, 60);
|
||||||
|
end
|
||||||
|
|
||||||
|
if not rates_per_ip:get(ip):poll(1) or not room.jibri_throttle:poll(1) then
|
||||||
|
module:log('warn', 'Filtering jibri start recording, ip:%s, room:%s stanza:%s',
|
||||||
|
ip, room.jid, tostring(stanza));
|
||||||
|
session.send(st.error_reply(stanza, 'wait', 'policy-violation'));
|
||||||
return true;
|
return true;
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
|
-- This module is enabled under the main virtual host
|
||||||
local new_throttle = require "util.throttle".create;
|
local new_throttle = require "util.throttle".create;
|
||||||
local st = require "util.stanza";
|
local st = require "util.stanza";
|
||||||
|
local jid = require "util.jid";
|
||||||
|
|
||||||
local token_util = module:require "token/util".new(module);
|
|
||||||
local util = module:require 'util';
|
local util = module:require 'util';
|
||||||
|
local is_admin = util.is_admin;
|
||||||
local room_jid_match_rewrite = util.room_jid_match_rewrite;
|
local room_jid_match_rewrite = util.room_jid_match_rewrite;
|
||||||
local is_feature_allowed = util.is_feature_allowed;
|
local is_feature_allowed = util.is_feature_allowed;
|
||||||
local is_sip_jigasi = util.is_sip_jigasi;
|
local is_sip_jigasi = util.is_sip_jigasi;
|
||||||
local get_room_from_jid = util.get_room_from_jid;
|
local get_room_from_jid = util.get_room_from_jid;
|
||||||
local is_healthcheck_room = util.is_healthcheck_room;
|
|
||||||
local process_host_module = util.process_host_module;
|
local process_host_module = util.process_host_module;
|
||||||
local jid_bare = require "util.jid".bare;
|
local jid_bare = require "util.jid".bare;
|
||||||
|
|
||||||
@ -22,17 +23,30 @@ if main_muc_component_host == nil then
|
|||||||
end
|
end
|
||||||
local main_muc_service;
|
local main_muc_service;
|
||||||
|
|
||||||
|
|
||||||
|
-- this is the main virtual host of the main prosody that this vnode serves
|
||||||
|
local main_domain = module:get_option_string('main_domain');
|
||||||
|
-- only the visitor prosody has main_domain setting
|
||||||
|
local is_visitor_prosody = main_domain ~= nil;
|
||||||
|
|
||||||
|
-- this is the main virtual host of this vnode
|
||||||
|
local local_domain = module:get_option_string('muc_mapper_domain_base');
|
||||||
|
|
||||||
|
local parentCtx = module:context(local_domain);
|
||||||
|
if parentCtx == nil then
|
||||||
|
log("error",
|
||||||
|
"Failed to start - unable to get parent context for host: %s",
|
||||||
|
tostring(local_domain));
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
local token_util = module:require "token/util".new(parentCtx);
|
||||||
|
|
||||||
-- no token configuration but required
|
-- no token configuration but required
|
||||||
if token_util == nil then
|
if token_util == nil then
|
||||||
module:log("error", "no token configuration but it is required");
|
module:log("error", "no token configuration but it is required");
|
||||||
return;
|
return;
|
||||||
end
|
end
|
||||||
|
|
||||||
local um_is_admin = require 'core.usermanager'.is_admin;
|
|
||||||
local function is_admin(jid)
|
|
||||||
return um_is_admin(jid, module.host);
|
|
||||||
end
|
|
||||||
|
|
||||||
-- The maximum number of simultaneous calls,
|
-- The maximum number of simultaneous calls,
|
||||||
-- and also the maximum number of new calls per minute that a session is allowed to create.
|
-- and also the maximum number of new calls per minute that a session is allowed to create.
|
||||||
local limit_outgoing_calls;
|
local limit_outgoing_calls;
|
||||||
@ -44,6 +58,8 @@ load_config();
|
|||||||
-- Header names to use to push extra data extracted from token, if any
|
-- 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_USER_ATTR_NAME = "X-outbound-call-initiator-user";
|
||||||
local OUT_INITIATOR_GROUP_ATTR_NAME = "X-outbound-call-initiator-group";
|
local OUT_INITIATOR_GROUP_ATTR_NAME = "X-outbound-call-initiator-group";
|
||||||
|
local OUT_ROOM_NAME_ATTR_NAME = "JvbRoomName";
|
||||||
|
|
||||||
local OUTGOING_CALLS_THROTTLE_INTERVAL = 60; -- if max_number_outgoing_calls is enabled it will be
|
local OUTGOING_CALLS_THROTTLE_INTERVAL = 60; -- if max_number_outgoing_calls is enabled it will be
|
||||||
-- the max number of outgoing calls a user can try for a minute
|
-- the max number of outgoing calls a user can try for a minute
|
||||||
|
|
||||||
@ -59,30 +75,45 @@ module:hook("pre-iq/full", function(event)
|
|||||||
local token = session.auth_token;
|
local token = session.auth_token;
|
||||||
|
|
||||||
-- find header with attr name 'JvbRoomName' and extract its value
|
-- find header with attr name 'JvbRoomName' and extract its value
|
||||||
local headerName = 'JvbRoomName';
|
|
||||||
local roomName;
|
local roomName;
|
||||||
for _, child in ipairs(dial.tags) do
|
-- Remove any 'header' element if it already exists, so it cannot be spoofed by a client
|
||||||
if (child.name == 'header'
|
dial:maptags(function(tag)
|
||||||
and child.attr.name == headerName) then
|
if tag.name == "header"
|
||||||
roomName = child.attr.value;
|
and (tag.attr.name == OUT_INITIATOR_USER_ATTR_NAME
|
||||||
break;
|
or tag.attr.name == OUT_INITIATOR_GROUP_ATTR_NAME) then
|
||||||
|
return nil
|
||||||
|
elseif tag.name == "header" and tag.attr.name == OUT_ROOM_NAME_ATTR_NAME then
|
||||||
|
roomName = tag.attr.value;
|
||||||
|
-- we will remove it as we will add it later, modified
|
||||||
|
if is_visitor_prosody then
|
||||||
|
return nil;
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
return tag
|
||||||
|
end);
|
||||||
|
|
||||||
|
local room_jid = jid.bare(stanza.attr.to);
|
||||||
|
local room_real_jid = room_jid_match_rewrite(room_jid);
|
||||||
|
local room = main_muc_service.get_room_from_jid(room_real_jid);
|
||||||
|
local is_sender_in_room = room:get_occupant_jid(stanza.attr.from) ~= nil;
|
||||||
|
|
||||||
|
if not room or not is_sender_in_room then
|
||||||
|
module:log("warn", "Filtering stanza dial, stanza:%s", tostring(stanza));
|
||||||
|
session.send(st.error_reply(stanza, "auth", "forbidden"));
|
||||||
|
return true;
|
||||||
end
|
end
|
||||||
|
|
||||||
local feature = dial.attr.to == 'jitsi_meet_transcribe' and 'transcription' or 'outbound-call';
|
local feature = dial.attr.to == 'jitsi_meet_transcribe' and 'transcription' or 'outbound-call';
|
||||||
local is_session_allowed = is_feature_allowed(session.jitsi_meet_context_features, feature);
|
local is_session_allowed = is_feature_allowed(
|
||||||
|
feature,
|
||||||
|
session.jitsi_meet_context_features,
|
||||||
|
session.granted_jitsi_meet_context_features,
|
||||||
|
room:get_affiliation(stanza.attr.from) == 'owner');
|
||||||
|
|
||||||
-- if current user is not allowed, but was granted moderation by a user
|
if roomName == nil
|
||||||
-- that is allowed by its features we want to allow it
|
or roomName ~= room_jid
|
||||||
local is_granting_session_allowed = false;
|
or (token ~= nil and not token_util:verify_room(session, room_real_jid))
|
||||||
if (session.granted_jitsi_meet_context_features) then
|
or not is_session_allowed
|
||||||
is_granting_session_allowed = is_feature_allowed(session.granted_jitsi_meet_context_features, feature);
|
|
||||||
end
|
|
||||||
|
|
||||||
if (token == nil
|
|
||||||
or roomName == nil
|
|
||||||
or not token_util:verify_room(session, room_jid_match_rewrite(roomName))
|
|
||||||
or not (is_session_allowed or is_granting_session_allowed))
|
|
||||||
then
|
then
|
||||||
module:log("warn", "Filtering stanza dial, stanza:%s", tostring(stanza));
|
module:log("warn", "Filtering stanza dial, stanza:%s", tostring(stanza));
|
||||||
session.send(st.error_reply(stanza, "auth", "forbidden"));
|
session.send(st.error_reply(stanza, "auth", "forbidden"));
|
||||||
@ -99,8 +130,8 @@ module:hook("pre-iq/full", function(event)
|
|||||||
group_id = session.granted_jitsi_meet_context_group_id;
|
group_id = session.granted_jitsi_meet_context_group_id;
|
||||||
end
|
end
|
||||||
|
|
||||||
-- now lets check any limits if configured
|
-- now lets check any limits for outgoing calls if configured
|
||||||
if limit_outgoing_calls > 0 then
|
if feature == 'outbound-call' and limit_outgoing_calls > 0 then
|
||||||
if not session.dial_out_throttle then
|
if not session.dial_out_throttle then
|
||||||
-- module:log("debug", "Enabling dial-out throttle session=%s.", session);
|
-- module:log("debug", "Enabling dial-out throttle session=%s.", session);
|
||||||
session.dial_out_throttle = new_throttle(limit_outgoing_calls, OUTGOING_CALLS_THROTTLE_INTERVAL);
|
session.dial_out_throttle = new_throttle(limit_outgoing_calls, OUTGOING_CALLS_THROTTLE_INTERVAL);
|
||||||
@ -119,25 +150,11 @@ module:hook("pre-iq/full", function(event)
|
|||||||
|
|
||||||
-- now lets insert token information if any
|
-- now lets insert token information if any
|
||||||
if session and user_id then
|
if session and user_id 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
|
-- adds initiator user id from token
|
||||||
dial:tag("header", {
|
dial:tag("header", {
|
||||||
xmlns = "urn:xmpp:rayo:1",
|
xmlns = "urn:xmpp:rayo:1",
|
||||||
name = OUT_INITIATOR_USER_ATTR_NAME,
|
name = OUT_INITIATOR_USER_ATTR_NAME,
|
||||||
value = user_id });
|
value = tostring(user_id)});
|
||||||
dial:up();
|
dial:up();
|
||||||
|
|
||||||
-- Add the initiator group information if it is present
|
-- Add the initiator group information if it is present
|
||||||
@ -145,13 +162,22 @@ module:hook("pre-iq/full", function(event)
|
|||||||
dial:tag("header", {
|
dial:tag("header", {
|
||||||
xmlns = "urn:xmpp:rayo:1",
|
xmlns = "urn:xmpp:rayo:1",
|
||||||
name = OUT_INITIATOR_GROUP_ATTR_NAME,
|
name = OUT_INITIATOR_GROUP_ATTR_NAME,
|
||||||
value = session.jitsi_meet_context_group });
|
value = tostring(session.jitsi_meet_context_group) });
|
||||||
dial:up();
|
dial:up();
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- we want to instruct jigasi to enter the main room, so send the correct main room jid
|
||||||
|
if is_visitor_prosody then
|
||||||
|
dial:tag("header", {
|
||||||
|
xmlns = "urn:xmpp:rayo:1",
|
||||||
|
name = OUT_ROOM_NAME_ATTR_NAME,
|
||||||
|
value = string.gsub(roomName, local_domain, main_domain) });
|
||||||
|
dial:up();
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end);
|
end, 1); -- make sure we run before domain mapper
|
||||||
|
|
||||||
--- Finds and returns the number of concurrent outgoing calls for a user
|
--- Finds and returns the number of concurrent outgoing calls for a user
|
||||||
-- @param context_user the user id extracted from the token
|
-- @param context_user the user id extracted from the token
|
||||||
@ -200,49 +226,10 @@ end
|
|||||||
|
|
||||||
module:hook_global('config-reloaded', load_config);
|
module:hook_global('config-reloaded', load_config);
|
||||||
|
|
||||||
function process_set_affiliation(event)
|
|
||||||
local actor, affiliation, jid, previous_affiliation, room
|
|
||||||
= event.actor, event.affiliation, event.jid, event.previous_affiliation, event.room;
|
|
||||||
local actor_session = sessions[actor];
|
|
||||||
|
|
||||||
if is_admin(jid) or is_healthcheck_room(room.jid) or not actor or not previous_affiliation
|
|
||||||
or not actor_session or not actor_session.jitsi_meet_context_features then
|
|
||||||
return;
|
|
||||||
end
|
|
||||||
|
|
||||||
local occupant;
|
|
||||||
for _, o in room:each_occupant() do
|
|
||||||
if o.bare_jid == jid then
|
|
||||||
occupant = o;
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if not occupant then
|
|
||||||
return;
|
|
||||||
end
|
|
||||||
|
|
||||||
local occupant_session = sessions[occupant.jid];
|
|
||||||
if not occupant_session then
|
|
||||||
return;
|
|
||||||
end
|
|
||||||
|
|
||||||
if previous_affiliation == 'none' and affiliation == 'owner' then
|
|
||||||
occupant_session.granted_jitsi_meet_context_features = actor_session.jitsi_meet_context_features;
|
|
||||||
occupant_session.granted_jitsi_meet_context_user_id = actor_session.jitsi_meet_context_user["id"];
|
|
||||||
occupant_session.granted_jitsi_meet_context_group_id = actor_session.jitsi_meet_context_group;
|
|
||||||
elseif previous_affiliation == 'owner' and ( affiliation == 'member' or affiliation == 'none' ) then
|
|
||||||
occupant_session.granted_jitsi_meet_context_features = nil;
|
|
||||||
occupant_session.granted_jitsi_meet_context_user_id = nil;
|
|
||||||
occupant_session.granted_jitsi_meet_context_group_id = nil;
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function process_main_muc_loaded(main_muc, host_module)
|
function process_main_muc_loaded(main_muc, host_module)
|
||||||
module:log('debug', 'Main muc loaded');
|
module:log('debug', 'Main muc loaded');
|
||||||
|
|
||||||
main_muc_service = main_muc;
|
main_muc_service = main_muc;
|
||||||
module:log("info", "Hook to muc events on %s", main_muc_component_host);
|
|
||||||
host_module:hook("muc-pre-set-affiliation", process_set_affiliation);
|
|
||||||
end
|
end
|
||||||
|
|
||||||
process_host_module(main_muc_component_host, function(host_module, host)
|
process_host_module(main_muc_component_host, function(host_module, host)
|
||||||
@ -259,3 +246,34 @@ process_host_module(main_muc_component_host, function(host_module, host)
|
|||||||
end);
|
end);
|
||||||
end
|
end
|
||||||
end);
|
end);
|
||||||
|
|
||||||
|
-- when recording participants may enable and backend transcriptions
|
||||||
|
-- it is possible that participant is not moderator, but has the features enabled for
|
||||||
|
-- transcribing, we need to allow that operation
|
||||||
|
module:hook('jitsi-metadata-allow-moderation', function (event)
|
||||||
|
local data, key, occupant, session = event.data, event.key, event.actor, event.session;
|
||||||
|
|
||||||
|
if key == 'recording' and data and data.isTranscribingEnabled ~= nil then
|
||||||
|
-- if it is recording we want to allow setting in metadata if not moderator but features
|
||||||
|
-- are present
|
||||||
|
if session.jitsi_meet_context_features
|
||||||
|
and occupant.role ~= 'moderator'
|
||||||
|
and is_feature_allowed('transcription', session.jitsi_meet_context_features)
|
||||||
|
and is_feature_allowed('recording', session.jitsi_meet_context_features) then
|
||||||
|
local res = {};
|
||||||
|
res.isTranscribingEnabled = data.isTranscribingEnabled;
|
||||||
|
return res;
|
||||||
|
elseif not session.jitsi_meet_context_features and occupant.role == 'moderator' then
|
||||||
|
return data;
|
||||||
|
else
|
||||||
|
return nil;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if occupant.role == 'moderator' then
|
||||||
|
return data;
|
||||||
|
end
|
||||||
|
|
||||||
|
return nil;
|
||||||
|
end);
|
||||||
|
|
||||||
|
42
roles/jitsi/files/prosody/modules/mod_filter_messages.lua
Normal file
42
roles/jitsi/files/prosody/modules/mod_filter_messages.lua
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
-- enable under the main muc module
|
||||||
|
-- a module that will filter group messages based on features (jitsi_meet_context_features)
|
||||||
|
-- when requested via metadata (permissions.groupChatRestricted)
|
||||||
|
local util = module:require 'util';
|
||||||
|
local get_room_from_jid = util.get_room_from_jid;
|
||||||
|
local st = require 'util.stanza';
|
||||||
|
|
||||||
|
local function on_message(event)
|
||||||
|
local stanza = event.stanza;
|
||||||
|
local body = stanza:get_child('body');
|
||||||
|
local session = event.origin;
|
||||||
|
|
||||||
|
if not body or not session then
|
||||||
|
-- we ignore messages without body - lobby, polls ...
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
|
||||||
|
-- get room name with tenant and find room.
|
||||||
|
-- this should already been through domain mapper and this should be the real room jid [tenant]name format
|
||||||
|
local room = get_room_from_jid(stanza.attr.to);
|
||||||
|
if not room then
|
||||||
|
module:log('warn', 'No room found found for %s', stanza.attr.to);
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
|
||||||
|
if room.jitsiMetadata and room.jitsiMetadata.permissions
|
||||||
|
and room.jitsiMetadata.permissions.groupChatRestricted
|
||||||
|
and not is_feature_allowed('send-groupchat', session.jitsi_meet_context_features) then
|
||||||
|
|
||||||
|
local reply = st.error_reply(stanza, 'cancel', 'not-allowed', 'Sending group messages not allowed');
|
||||||
|
if session.type == 's2sin' or session.type == 's2sout' then
|
||||||
|
reply.skipMapping = true;
|
||||||
|
end
|
||||||
|
module:send(reply);
|
||||||
|
|
||||||
|
-- let's filter this message
|
||||||
|
return true;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
module:hook('message/bare', on_message); -- room messages
|
||||||
|
module:hook('jitsi-visitor-groupchat-pre-route', on_message); -- visitors messages
|
@ -14,8 +14,11 @@ local jid = require 'util.jid';
|
|||||||
local st = require 'util.stanza';
|
local st = require 'util.stanza';
|
||||||
local new_id = require 'util.id'.medium;
|
local new_id = require 'util.id'.medium;
|
||||||
local filters = require 'util.filters';
|
local filters = require 'util.filters';
|
||||||
|
local array = require 'util.array';
|
||||||
|
local set = require 'util.set';
|
||||||
|
|
||||||
local util = module:require 'util';
|
local util = module:require 'util';
|
||||||
|
local is_admin = util.is_admin;
|
||||||
local ends_with = util.ends_with;
|
local ends_with = util.ends_with;
|
||||||
local is_vpaas = util.is_vpaas;
|
local is_vpaas = util.is_vpaas;
|
||||||
local room_jid_match_rewrite = util.room_jid_match_rewrite;
|
local room_jid_match_rewrite = util.room_jid_match_rewrite;
|
||||||
@ -23,6 +26,12 @@ local get_room_from_jid = util.get_room_from_jid;
|
|||||||
local get_focus_occupant = util.get_focus_occupant;
|
local get_focus_occupant = util.get_focus_occupant;
|
||||||
local internal_room_jid_match_rewrite = util.internal_room_jid_match_rewrite;
|
local internal_room_jid_match_rewrite = util.internal_room_jid_match_rewrite;
|
||||||
local presence_check_status = util.presence_check_status;
|
local presence_check_status = util.presence_check_status;
|
||||||
|
local respond_iq_result = util.respond_iq_result;
|
||||||
|
|
||||||
|
local PARTICIPANT_PROP_RAISE_HAND = 'jitsi_participant_raisedHand';
|
||||||
|
local PARTICIPANT_PROP_REQUEST_TRANSCRIPTION = 'jitsi_participant_requestingTranscription';
|
||||||
|
local PARTICIPANT_PROP_TRANSLATION_LANG = 'jitsi_participant_translation_language';
|
||||||
|
local TRANSCRIPT_DEFAULT_LANG = module:get_option_string('transcriptions_default_language', 'en');
|
||||||
|
|
||||||
-- this is the main virtual host of this vnode
|
-- this is the main virtual host of this vnode
|
||||||
local local_domain = module:get_option_string('muc_mapper_domain_base');
|
local local_domain = module:get_option_string('muc_mapper_domain_base');
|
||||||
@ -43,6 +52,9 @@ local local_muc_domain = muc_domain_prefix..'.'..local_domain;
|
|||||||
|
|
||||||
local NICK_NS = 'http://jabber.org/protocol/nick';
|
local NICK_NS = 'http://jabber.org/protocol/nick';
|
||||||
|
|
||||||
|
-- in certain cases we consider participants with token as moderators, this is the default behavior which can be turned off
|
||||||
|
local auto_promoted_with_token = module:get_option_boolean('visitors_auto_promoted_with_token', true);
|
||||||
|
|
||||||
-- we send stats for the total number of rooms, total number of participants and total number of visitors
|
-- we send stats for the total number of rooms, total number of participants and total number of visitors
|
||||||
local measure_rooms = module:measure('vnode-rooms', 'amount');
|
local measure_rooms = module:measure('vnode-rooms', 'amount');
|
||||||
local measure_participants = module:measure('vnode-participants', 'amount');
|
local measure_participants = module:measure('vnode-participants', 'amount');
|
||||||
@ -52,17 +64,72 @@ local sent_iq_cache = require 'util.cache'.new(200);
|
|||||||
|
|
||||||
local sessions = prosody.full_sessions;
|
local sessions = prosody.full_sessions;
|
||||||
|
|
||||||
local um_is_admin = require 'core.usermanager'.is_admin;
|
local function send_transcriptions_update(room)
|
||||||
local function is_admin(jid)
|
-- let's notify main prosody
|
||||||
return um_is_admin(jid, module.host);
|
local lang_array = array();
|
||||||
|
local count = 0;
|
||||||
|
|
||||||
|
for k, v in pairs(room._transcription_languages) do
|
||||||
|
lang_array:push(v);
|
||||||
|
count = count + 1;
|
||||||
|
end
|
||||||
|
|
||||||
|
local iq_id = new_id();
|
||||||
|
sent_iq_cache:set(iq_id, socket.gettime());
|
||||||
|
module:send(st.iq({
|
||||||
|
type = 'set',
|
||||||
|
to = 'visitors.'..main_domain,
|
||||||
|
from = local_domain,
|
||||||
|
id = iq_id })
|
||||||
|
:tag('visitors', { xmlns = 'jitsi:visitors',
|
||||||
|
room = jid.join(jid.node(room.jid), muc_domain_prefix..'.'..main_domain) })
|
||||||
|
:tag('transcription-languages', {
|
||||||
|
xmlns = 'jitsi:visitors',
|
||||||
|
langs = lang_array:unique():sort():concat(','),
|
||||||
|
count = tostring(count)
|
||||||
|
}):up());
|
||||||
|
end
|
||||||
|
|
||||||
|
local function remove_transcription(room, occupant)
|
||||||
|
local send_update = false;
|
||||||
|
if room._transcription_languages then
|
||||||
|
if room._transcription_languages[occupant.jid] then
|
||||||
|
send_update = true;
|
||||||
|
end
|
||||||
|
room._transcription_languages[occupant.jid] = nil;
|
||||||
|
end
|
||||||
|
|
||||||
|
if send_update then
|
||||||
|
send_transcriptions_update(room);
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- if lang is nil we will remove it from the list
|
||||||
|
local function add_transcription(room, occupant, lang)
|
||||||
|
if not room._transcription_languages then
|
||||||
|
room._transcription_languages = {};
|
||||||
|
end
|
||||||
|
|
||||||
|
local old = room._transcription_languages[occupant.jid];
|
||||||
|
room._transcription_languages[occupant.jid] = lang or TRANSCRIPT_DEFAULT_LANG;
|
||||||
|
|
||||||
|
if old ~= room._transcription_languages[occupant.jid] then
|
||||||
|
send_transcriptions_update(room);
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- mark all occupants as visitors
|
-- mark all occupants as visitors
|
||||||
module:hook('muc-occupant-pre-join', function (event)
|
module:hook('muc-occupant-pre-join', function (event)
|
||||||
local occupant, room, origin, stanza = event.occupant, event.room, event.origin, event.stanza;
|
local occupant, room, origin, stanza = event.occupant, event.room, event.origin, event.stanza;
|
||||||
local node, host = jid.split(occupant.bare_jid);
|
local node, host = jid.split(occupant.bare_jid);
|
||||||
|
local resource = jid.resource(occupant.nick);
|
||||||
|
|
||||||
if prosody.hosts[host] and not is_admin(occupant.bare_jid) then
|
if is_admin(occupant.bare_jid) then
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
|
||||||
|
if prosody.hosts[host] then
|
||||||
|
-- local participants which host is defined in this prosody
|
||||||
if room._main_room_lobby_enabled then
|
if room._main_room_lobby_enabled then
|
||||||
origin.send(st.error_reply(stanza, 'cancel', 'not-allowed', 'Visitors not allowed while lobby is on!')
|
origin.send(st.error_reply(stanza, 'cancel', 'not-allowed', 'Visitors not allowed while lobby is on!')
|
||||||
:tag('no-visitors-lobby', { xmlns = 'jitsi:visitors' }));
|
:tag('no-visitors-lobby', { xmlns = 'jitsi:visitors' }));
|
||||||
@ -70,6 +137,9 @@ module:hook('muc-occupant-pre-join', function (event)
|
|||||||
else
|
else
|
||||||
occupant.role = 'visitor';
|
occupant.role = 'visitor';
|
||||||
end
|
end
|
||||||
|
elseif room.moderators_list and room.moderators_list:contains(resource) then
|
||||||
|
-- remote participants, host is the main prosody
|
||||||
|
occupant.role = 'moderator';
|
||||||
end
|
end
|
||||||
end, 3);
|
end, 3);
|
||||||
|
|
||||||
@ -89,7 +159,7 @@ module:hook('muc-occupant-pre-leave', function (event)
|
|||||||
-- to main prosody
|
-- to main prosody
|
||||||
local pr = occupant:get_presence();
|
local pr = occupant:get_presence();
|
||||||
|
|
||||||
local raiseHand = pr:get_child_text('jitsi_participant_raisedHand');
|
local raiseHand = pr:get_child_text(PARTICIPANT_PROP_RAISE_HAND);
|
||||||
|
|
||||||
-- a promotion detected let's send it to main prosody
|
-- a promotion detected let's send it to main prosody
|
||||||
if raiseHand and #raiseHand > 0 then
|
if raiseHand and #raiseHand > 0 then
|
||||||
@ -111,6 +181,7 @@ module:hook('muc-occupant-pre-leave', function (event)
|
|||||||
module:send(promotion_request);
|
module:send(promotion_request);
|
||||||
end
|
end
|
||||||
|
|
||||||
|
remove_transcription(room, occupant);
|
||||||
end, 1); -- rate limit is 0
|
end, 1); -- rate limit is 0
|
||||||
|
|
||||||
-- Returns the main participants count and the visitors count
|
-- Returns the main participants count and the visitors count
|
||||||
@ -136,6 +207,20 @@ local function cancel_destroy_timer(room)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function destroy_with_conference_ended(room)
|
||||||
|
-- if the room is being destroyed, ignore
|
||||||
|
if room.destroying then
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
|
||||||
|
cancel_destroy_timer(room);
|
||||||
|
|
||||||
|
local main_count, visitors_count = get_occupant_counts(room);
|
||||||
|
module:log('info', 'Will destroy:%s main_occupants:%s visitors:%s', room.jid, main_count, visitors_count);
|
||||||
|
room:destroy(nil, 'Conference ended.');
|
||||||
|
return true;
|
||||||
|
end
|
||||||
|
|
||||||
-- schedules a new destroy timer which will destroy the room if there are no visitors after the timeout
|
-- schedules a new destroy timer which will destroy the room if there are no visitors after the timeout
|
||||||
local function schedule_destroy_timer(room)
|
local function schedule_destroy_timer(room)
|
||||||
cancel_destroy_timer(room);
|
cancel_destroy_timer(room);
|
||||||
@ -165,7 +250,9 @@ module:hook('muc-occupant-left', function (event)
|
|||||||
if prosody.hosts[occupant_domain] and not is_admin(occupant.bare_jid) then
|
if prosody.hosts[occupant_domain] and not is_admin(occupant.bare_jid) then
|
||||||
local focus_occupant = get_focus_occupant(room);
|
local focus_occupant = get_focus_occupant(room);
|
||||||
if not focus_occupant then
|
if not focus_occupant then
|
||||||
module:log('info', 'No focus found for %s', room.jid);
|
if not room.destroying then
|
||||||
|
module:log('warn', 'No focus found for %s', room.jid);
|
||||||
|
end
|
||||||
return;
|
return;
|
||||||
end
|
end
|
||||||
-- Let's forward unavailable presence to the special jicofo
|
-- Let's forward unavailable presence to the special jicofo
|
||||||
@ -194,10 +281,15 @@ module:hook('muc-occupant-left', function (event)
|
|||||||
if visitors_count == 0 then
|
if visitors_count == 0 then
|
||||||
schedule_destroy_timer(room);
|
schedule_destroy_timer(room);
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if main_count == 0 then
|
||||||
|
destroy_with_conference_ended(room);
|
||||||
|
end
|
||||||
end);
|
end);
|
||||||
|
|
||||||
-- forward visitor presences to jicofo
|
-- forward visitor presences to jicofo
|
||||||
-- detects raise hand in visitors presence, this is request for promotion
|
-- detects raise hand in visitors presence, this is request for promotion
|
||||||
|
-- detects the requested transcription and its language to send updates for it
|
||||||
module:hook('muc-broadcast-presence', function (event)
|
module:hook('muc-broadcast-presence', function (event)
|
||||||
local occupant = event.occupant;
|
local occupant = event.occupant;
|
||||||
|
|
||||||
@ -227,10 +319,11 @@ module:hook('muc-broadcast-presence', function (event)
|
|||||||
full_p.attr.to = focus_occupant.jid;
|
full_p.attr.to = focus_occupant.jid;
|
||||||
room:route_to_occupant(focus_occupant, full_p);
|
room:route_to_occupant(focus_occupant, full_p);
|
||||||
|
|
||||||
local raiseHand = full_p:get_child_text('jitsi_participant_raisedHand');
|
local raiseHand = full_p:get_child_text(PARTICIPANT_PROP_RAISE_HAND);
|
||||||
-- a promotion detected let's send it to main prosody
|
-- a promotion detected let's send it to main prosody
|
||||||
if raiseHand then
|
if raiseHand then
|
||||||
local user_id;
|
local user_id;
|
||||||
|
local group_id;
|
||||||
local is_moderator;
|
local is_moderator;
|
||||||
local session = sessions[occupant.jid];
|
local session = sessions[occupant.jid];
|
||||||
local identity = session and session.jitsi_meet_context_user;
|
local identity = session and session.jitsi_meet_context_user;
|
||||||
@ -246,14 +339,14 @@ module:hook('muc-broadcast-presence', function (event)
|
|||||||
-- so we can be auto promoted
|
-- so we can be auto promoted
|
||||||
if identity and identity.id then
|
if identity and identity.id then
|
||||||
user_id = session.jitsi_meet_context_user.id;
|
user_id = session.jitsi_meet_context_user.id;
|
||||||
|
group_id = session.jitsi_meet_context_group;
|
||||||
|
|
||||||
if room._data.moderator_id then
|
if session.auth_token and auto_promoted_with_token then
|
||||||
if room._data.moderator_id == user_id then
|
if not session.jitsi_meet_tenant_mismatch or session.jitsi_web_query_prefix == '' then
|
||||||
|
-- non-vpaas and having a token is considered a moderator, and if it is not in '/' tenant
|
||||||
|
-- the tenant from url and token should match
|
||||||
is_moderator = true;
|
is_moderator = true;
|
||||||
end
|
end
|
||||||
elseif session.auth_token then
|
|
||||||
-- non-vpass and having a token is considered a moderator
|
|
||||||
is_moderator = true;
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@ -272,6 +365,7 @@ module:hook('muc-broadcast-presence', function (event)
|
|||||||
jid = occupant.jid,
|
jid = occupant.jid,
|
||||||
time = raiseHand,
|
time = raiseHand,
|
||||||
userId = user_id,
|
userId = user_id,
|
||||||
|
groupId = group_id,
|
||||||
forcePromote = is_moderator and 'true' or 'false';
|
forcePromote = is_moderator and 'true' or 'false';
|
||||||
}):up();
|
}):up();
|
||||||
|
|
||||||
@ -283,6 +377,18 @@ module:hook('muc-broadcast-presence', function (event)
|
|||||||
module:send(promotion_request);
|
module:send(promotion_request);
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local requestTranscriptionValue = full_p:get_child_text(PARTICIPANT_PROP_REQUEST_TRANSCRIPTION);
|
||||||
|
local hasTranscriptionEnabled = room._transcription_languages and room._transcription_languages[occupant.jid];
|
||||||
|
|
||||||
|
-- detect transcription
|
||||||
|
if requestTranscriptionValue == 'true' then
|
||||||
|
local lang = full_p:get_child_text(PARTICIPANT_PROP_TRANSLATION_LANG);
|
||||||
|
|
||||||
|
add_transcription(room, occupant, lang);
|
||||||
|
elseif hasTranscriptionEnabled then
|
||||||
|
remove_transcription(room, occupant, nil);
|
||||||
|
end
|
||||||
|
|
||||||
return;
|
return;
|
||||||
end);
|
end);
|
||||||
|
|
||||||
@ -317,7 +423,6 @@ local function stanza_handler(event)
|
|||||||
local room = get_room_from_jid(room_jid_match_rewrite(room_jid));
|
local room = get_room_from_jid(room_jid_match_rewrite(room_jid));
|
||||||
|
|
||||||
if not room then
|
if not room then
|
||||||
module:log('warn', 'No room found %s', room_jid);
|
|
||||||
return;
|
return;
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -327,12 +432,7 @@ local function stanza_handler(event)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- respond with successful receiving the iq
|
-- respond with successful receiving the iq
|
||||||
origin.send(st.iq({
|
respond_iq_result(origin, stanza);
|
||||||
type = 'result';
|
|
||||||
from = stanza.attr.to;
|
|
||||||
to = stanza.attr.from;
|
|
||||||
id = stanza.attr.id
|
|
||||||
}));
|
|
||||||
|
|
||||||
local req_jid = request_promotion.attr.jid;
|
local req_jid = request_promotion.attr.jid;
|
||||||
-- now let's find the occupant and forward the response
|
-- now let's find the occupant and forward the response
|
||||||
@ -469,7 +569,15 @@ module:hook('jicofo-unlock-room', function(e)
|
|||||||
return true;
|
return true;
|
||||||
end);
|
end);
|
||||||
|
|
||||||
-- handles incoming iq connect stanzas
|
-- handles incoming iq visitors stanzas
|
||||||
|
-- connect - sent after sending all main participant's presences
|
||||||
|
-- disconnect - sent when main room is destroyed or when we receive a 'disconnect-vnode' iq from jicofo
|
||||||
|
-- update - sent on:
|
||||||
|
-- * room secret is changed
|
||||||
|
-- * lobby enabled or disabled
|
||||||
|
-- * initially before connect to report currently joined moderators
|
||||||
|
-- * moderator participant joins main room
|
||||||
|
-- * a participant has been granted moderator rights
|
||||||
local function iq_from_main_handler(event)
|
local function iq_from_main_handler(event)
|
||||||
local origin, stanza = event.origin, event.stanza;
|
local origin, stanza = event.origin, event.stanza;
|
||||||
|
|
||||||
@ -500,7 +608,7 @@ local function iq_from_main_handler(event)
|
|||||||
local room = get_room_from_jid(room_jid_match_rewrite(room_jid));
|
local room = get_room_from_jid(room_jid_match_rewrite(room_jid));
|
||||||
|
|
||||||
if not room then
|
if not room then
|
||||||
module:log('warn', 'No room found %s', room_jid);
|
module:log('warn', 'No room found %s in iq_from_main_handler for:%s', room_jid, visitors_iq);
|
||||||
return;
|
return;
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -523,27 +631,16 @@ local function iq_from_main_handler(event)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- respond with successful receiving the iq
|
-- respond with successful receiving the iq
|
||||||
origin.send(st.iq({
|
respond_iq_result(origin, stanza);
|
||||||
type = 'result';
|
|
||||||
from = stanza.attr.to;
|
|
||||||
to = stanza.attr.from;
|
|
||||||
id = stanza.attr.id
|
|
||||||
}));
|
|
||||||
|
|
||||||
if process_disconnect then
|
if process_disconnect then
|
||||||
cancel_destroy_timer(room);
|
return destroy_with_conference_ended(room);
|
||||||
|
|
||||||
local main_count, visitors_count = get_occupant_counts(room);
|
|
||||||
module:log('info', 'Will destroy:%s main_occupants:%s visitors:%s', room.jid, main_count, visitors_count);
|
|
||||||
room:destroy(nil, 'Conference ended.');
|
|
||||||
return true;
|
|
||||||
end
|
end
|
||||||
|
|
||||||
-- if there is password supplied use it
|
-- if there is password supplied use it
|
||||||
-- if this is update it will either set or remove the password
|
-- if this is update it will either set or remove the password
|
||||||
room:set_password(node.attr.password);
|
room:set_password(node.attr.password);
|
||||||
room._data.meetingId = node.attr.meetingId;
|
room._data.meetingId = node.attr.meetingId;
|
||||||
room._data.moderator_id = node.attr.moderatorId;
|
|
||||||
local createdTimestamp = node.attr.createdTimestamp;
|
local createdTimestamp = node.attr.createdTimestamp;
|
||||||
room.created_timestamp = createdTimestamp and tonumber(createdTimestamp) or nil;
|
room.created_timestamp = createdTimestamp and tonumber(createdTimestamp) or nil;
|
||||||
|
|
||||||
@ -553,6 +650,28 @@ local function iq_from_main_handler(event)
|
|||||||
room._main_room_lobby_enabled = false;
|
room._main_room_lobby_enabled = false;
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- read the moderators list
|
||||||
|
room.moderators_list = room.moderators_list or set.new();
|
||||||
|
local moderators = node:get_child('moderators');
|
||||||
|
|
||||||
|
if moderators then
|
||||||
|
for _, child in ipairs(moderators.tags) do
|
||||||
|
if child.name == 'item' then
|
||||||
|
room.moderators_list:add(child.attr.epId);
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- let's check current occupants roles and promote them if needed
|
||||||
|
-- we change only main participants which are not moderators, but participant
|
||||||
|
for _, o in room:each_occupant() do
|
||||||
|
if not is_admin(o.bare_jid)
|
||||||
|
and o.role == 'participant'
|
||||||
|
and room.moderators_list:contains(jid.resource(o.nick)) then
|
||||||
|
room:set_affiliation(true, o.bare_jid, 'owner');
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
if fire_jicofo_unlock then
|
if fire_jicofo_unlock then
|
||||||
-- everything is connected allow participants to join
|
-- everything is connected allow participants to join
|
||||||
module:fire_event('jicofo-unlock-room', { room = room; fmuc_fired = true; });
|
module:fire_event('jicofo-unlock-room', { room = room; fmuc_fired = true; });
|
||||||
|
@ -44,10 +44,12 @@ local stanza = event.stanza;
|
|||||||
|
|
||||||
if session.jitsi_meet_context_user ~= nil then
|
if session.jitsi_meet_context_user ~= nil then
|
||||||
initiator.id = session.jitsi_meet_context_user.id;
|
initiator.id = session.jitsi_meet_context_user.id;
|
||||||
|
else
|
||||||
|
initiator.id = session.granted_jitsi_meet_context_user_id;
|
||||||
end
|
end
|
||||||
if session.jitsi_meet_context_group ~= nil then
|
|
||||||
initiator.group = session.jitsi_meet_context_group;
|
initiator.group
|
||||||
end
|
= session.jitsi_meet_context_group or session.granted_jitsi_meet_context_group_id;
|
||||||
|
|
||||||
app_data.file_recording_metadata.initiator = initiator
|
app_data.file_recording_metadata.initiator = initiator
|
||||||
update_app_data = true;
|
update_app_data = true;
|
||||||
|
201
roles/jitsi/files/prosody/modules/mod_jitsi_permissions.lua
Normal file
201
roles/jitsi/files/prosody/modules/mod_jitsi_permissions.lua
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
-- this is auto loaded by meeting_id
|
||||||
|
local filters = require 'util.filters';
|
||||||
|
local jid = require 'util.jid';
|
||||||
|
|
||||||
|
local util = module:require 'util';
|
||||||
|
local is_admin = util.is_admin;
|
||||||
|
local get_room_from_jid = util.get_room_from_jid;
|
||||||
|
local is_healthcheck_room = util.is_healthcheck_room;
|
||||||
|
local room_jid_match_rewrite = util.room_jid_match_rewrite;
|
||||||
|
local ends_with = util.ends_with;
|
||||||
|
local presence_check_status = util.presence_check_status;
|
||||||
|
|
||||||
|
local MUC_NS = 'http://jabber.org/protocol/muc';
|
||||||
|
|
||||||
|
local muc_domain_prefix = module:get_option_string('muc_mapper_domain_prefix', 'conference');
|
||||||
|
|
||||||
|
local muc_domain_base = module:get_option_string('muc_mapper_domain_base');
|
||||||
|
if not muc_domain_base then
|
||||||
|
module:log('warn', 'No "muc_domain_base" option set, disabling kick check endpoint.');
|
||||||
|
return ;
|
||||||
|
end
|
||||||
|
|
||||||
|
-- only the visitor prosody has main_domain setting
|
||||||
|
local is_visitor_prosody = module:get_option_string('main_domain') ~= nil;
|
||||||
|
|
||||||
|
-- load it only on the main muc component as it is loaded by muc_meeting_id which is loaded and for the breakout room muc
|
||||||
|
if muc_domain_prefix..'.'..muc_domain_base ~= module.host or is_visitor_prosody then
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
|
||||||
|
local sessions = prosody.full_sessions;
|
||||||
|
local default_permissions;
|
||||||
|
|
||||||
|
local function load_config()
|
||||||
|
default_permissions = module:get_option('jitsi_default_permissions', {
|
||||||
|
livestreaming = true;
|
||||||
|
recording = true;
|
||||||
|
transcription = true;
|
||||||
|
['outbound-call'] = true;
|
||||||
|
['create-polls'] = true;
|
||||||
|
['send-groupchat'] = true;
|
||||||
|
flip = true;
|
||||||
|
});
|
||||||
|
end
|
||||||
|
load_config();
|
||||||
|
|
||||||
|
function process_set_affiliation(event)
|
||||||
|
local actor, affiliation, jid, previous_affiliation, room
|
||||||
|
= event.actor, event.affiliation, event.jid, event.previous_affiliation, event.room;
|
||||||
|
local actor_session = sessions[actor];
|
||||||
|
|
||||||
|
if is_admin(jid) or is_healthcheck_room(room.jid) or not actor or not previous_affiliation
|
||||||
|
or not actor_session or not actor_session.jitsi_meet_context_features then
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
|
||||||
|
local occupant;
|
||||||
|
for _, o in room:each_occupant() do
|
||||||
|
if o.bare_jid == jid then
|
||||||
|
occupant = o;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not occupant then
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
|
||||||
|
local occupant_session = sessions[occupant.jid];
|
||||||
|
if not occupant_session then
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
|
||||||
|
if previous_affiliation == 'none' and affiliation == 'owner' then
|
||||||
|
occupant_session.granted_jitsi_meet_context_features = actor_session.jitsi_meet_context_features;
|
||||||
|
if actor_session.jitsi_meet_context_user then
|
||||||
|
occupant_session.granted_jitsi_meet_context_user_id = actor_session.jitsi_meet_context_user['id']
|
||||||
|
or actor_session.granted_jitsi_meet_context_user_id;
|
||||||
|
end
|
||||||
|
occupant_session.granted_jitsi_meet_context_group_id = actor_session.jitsi_meet_context_group
|
||||||
|
or actor_session.granted_jitsi_meet_context_group_id;
|
||||||
|
elseif previous_affiliation == 'owner' and ( affiliation == 'member' or affiliation == 'none' ) then
|
||||||
|
occupant_session.granted_jitsi_meet_context_features = nil;
|
||||||
|
occupant_session.granted_jitsi_meet_context_user_id = nil;
|
||||||
|
occupant_session.granted_jitsi_meet_context_group_id = nil;
|
||||||
|
|
||||||
|
-- on revoke
|
||||||
|
if not session.auth_token then
|
||||||
|
occupant_session.jitsi_meet_context_features = nil;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Detects when sending self-presence because of role change
|
||||||
|
-- we can end up here because of the following cases:
|
||||||
|
-- 1. user joins the room and is granted moderator by another moderator or jicofo
|
||||||
|
-- 2. Some module changes the role of the user by using set_affiliation method
|
||||||
|
-- In cases where authentication is 'anonymous', 'jitsi-anonymous', 'internal_hashed', 'internal_plain', 'cyrus' we
|
||||||
|
-- want to send default permissions all to indicate UI that everything is allowed (to not relay on the UI to check
|
||||||
|
-- is participant moderator or not), to allow finer control over the permissions.
|
||||||
|
-- In case the authentication is 'token' based we want to send permissions only if the token of the user does not include
|
||||||
|
-- features in the user.context.
|
||||||
|
-- In case of allowners we want to send the permissions, no matter of the authentication method.
|
||||||
|
-- In case permissions were granted we want to send the granted permissions in all cases except when the user is
|
||||||
|
-- using token that has features pre-defined (authentication is 'token').
|
||||||
|
function filter_stanza(stanza, session)
|
||||||
|
if not stanza.attr or not stanza.attr.to or stanza.name ~= 'presence'
|
||||||
|
or stanza.attr.type == 'unavailable' or ends_with(stanza.attr.from, '/focus') then
|
||||||
|
return stanza;
|
||||||
|
end
|
||||||
|
|
||||||
|
local bare_to = jid.bare(stanza.attr.to);
|
||||||
|
if is_admin(bare_to) then
|
||||||
|
return stanza;
|
||||||
|
end
|
||||||
|
|
||||||
|
local muc_x = stanza:get_child('x', MUC_NS..'#user');
|
||||||
|
if not muc_x or not presence_check_status(muc_x, '110') then
|
||||||
|
return stanza;
|
||||||
|
end
|
||||||
|
|
||||||
|
local room = get_room_from_jid(room_jid_match_rewrite(jid.bare(stanza.attr.from)));
|
||||||
|
|
||||||
|
if not room or is_healthcheck_room(room.jid) then
|
||||||
|
return stanza;
|
||||||
|
end
|
||||||
|
|
||||||
|
if not room.send_default_permissions_to then
|
||||||
|
room.send_default_permissions_to = {};
|
||||||
|
end
|
||||||
|
|
||||||
|
if not session.force_permissions_update then
|
||||||
|
if session.auth_token and session.jitsi_meet_context_features then -- token and features are set so skip
|
||||||
|
room.send_default_permissions_to[bare_to] = nil;
|
||||||
|
return stanza;
|
||||||
|
end
|
||||||
|
|
||||||
|
-- we are sending permissions only when becoming a member
|
||||||
|
local is_moderator = false;
|
||||||
|
for item in muc_x:childtags('item') do
|
||||||
|
if item.attr.role == 'moderator' then
|
||||||
|
is_moderator = true;
|
||||||
|
break;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not is_moderator then
|
||||||
|
return stanza;
|
||||||
|
end
|
||||||
|
|
||||||
|
if not room.send_default_permissions_to[bare_to] then
|
||||||
|
return stanza;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
session.force_permissions_update = false;
|
||||||
|
|
||||||
|
local permissions_to_send
|
||||||
|
= session.jitsi_meet_context_features or session.granted_jitsi_meet_context_features or default_permissions;
|
||||||
|
|
||||||
|
room.send_default_permissions_to[bare_to] = nil;
|
||||||
|
|
||||||
|
if not session.granted_jitsi_meet_context_features and not session.jitsi_meet_context_features then
|
||||||
|
session.jitsi_meet_context_features = {};
|
||||||
|
end
|
||||||
|
|
||||||
|
stanza:tag('permissions', { xmlns='http://jitsi.org/jitmeet' });
|
||||||
|
for k, v in pairs(permissions_to_send) do
|
||||||
|
local val = tostring(v);
|
||||||
|
stanza:tag('p', { name = k, val = val }):up();
|
||||||
|
if session.jitsi_meet_context_features then
|
||||||
|
session.jitsi_meet_context_features[k] = val;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
stanza:up();
|
||||||
|
|
||||||
|
return stanza;
|
||||||
|
end
|
||||||
|
|
||||||
|
-- we need to indicate that we will send permissions if we need to
|
||||||
|
-- we need to handle granted features and stuff in the pre-set hook so they are unavailable
|
||||||
|
-- when the self presence is set, so we can update the client, the checks
|
||||||
|
-- whether the actor is allowed to set the affiliation are done before pre-set hook is fired
|
||||||
|
module:hook('muc-pre-set-affiliation', function(event)
|
||||||
|
local jid, room = event.jid, event.room;
|
||||||
|
|
||||||
|
if not room.send_default_permissions_to then
|
||||||
|
room.send_default_permissions_to = {};
|
||||||
|
end
|
||||||
|
room.send_default_permissions_to[jid] = true;
|
||||||
|
|
||||||
|
process_set_affiliation(event);
|
||||||
|
end);
|
||||||
|
|
||||||
|
function filter_session(session)
|
||||||
|
-- domain mapper is filtering on default priority 0
|
||||||
|
-- allowners is -1 and we need it after that
|
||||||
|
filters.add_filter(session, 'stanzas/out', filter_stanza, -2);
|
||||||
|
end
|
||||||
|
|
||||||
|
-- enable filtering presences
|
||||||
|
filters.add_filter_hook(filter_session);
|
@ -3,6 +3,7 @@
|
|||||||
module:set_global();
|
module:set_global();
|
||||||
|
|
||||||
local formdecode = require "util.http".formdecode;
|
local formdecode = require "util.http".formdecode;
|
||||||
|
local region_header_name = module:get_option_string('region_header_name', 'x_proxy_region');
|
||||||
|
|
||||||
-- Extract the following parameters from the URL and set them in the session:
|
-- Extract the following parameters from the URL and set them in the session:
|
||||||
-- * previd: for session resumption
|
-- * previd: for session resumption
|
||||||
@ -24,6 +25,8 @@ function init_session(event)
|
|||||||
session.jitsi_web_query_room = params.room;
|
session.jitsi_web_query_room = params.room;
|
||||||
session.jitsi_web_query_prefix = params.prefix or "";
|
session.jitsi_web_query_prefix = params.prefix or "";
|
||||||
end
|
end
|
||||||
|
|
||||||
|
session.user_region = request.headers[region_header_name];
|
||||||
end
|
end
|
||||||
|
|
||||||
module:hook_global("bosh-session", init_session, 1);
|
module:hook_global("bosh-session", init_session, 1);
|
||||||
|
@ -1,13 +1,15 @@
|
|||||||
|
--- activate under the main muc component
|
||||||
local filters = require 'util.filters';
|
local filters = require 'util.filters';
|
||||||
local jid = require "util.jid";
|
local jid = require "util.jid";
|
||||||
local jid_bare = require "util.jid".bare;
|
local jid_bare = require "util.jid".bare;
|
||||||
local jid_host = require "util.jid".host;
|
local jid_host = require "util.jid".host;
|
||||||
local st = require "util.stanza";
|
local st = require "util.stanza";
|
||||||
local um_is_admin = require "core.usermanager".is_admin;
|
|
||||||
local util = module:require "util";
|
local util = module:require "util";
|
||||||
|
local is_admin = util.is_admin;
|
||||||
local is_healthcheck_room = util.is_healthcheck_room;
|
local is_healthcheck_room = util.is_healthcheck_room;
|
||||||
local is_moderated = util.is_moderated;
|
local is_moderated = util.is_moderated;
|
||||||
local get_room_from_jid = util.get_room_from_jid;
|
local get_room_from_jid = util.get_room_from_jid;
|
||||||
|
local room_jid_match_rewrite = util.room_jid_match_rewrite;
|
||||||
local presence_check_status = util.presence_check_status;
|
local presence_check_status = util.presence_check_status;
|
||||||
local MUC_NS = 'http://jabber.org/protocol/muc';
|
local MUC_NS = 'http://jabber.org/protocol/muc';
|
||||||
|
|
||||||
@ -18,15 +20,19 @@ local function load_config()
|
|||||||
end
|
end
|
||||||
load_config();
|
load_config();
|
||||||
|
|
||||||
local function is_admin(_jid)
|
|
||||||
return um_is_admin(_jid, module.host);
|
|
||||||
end
|
|
||||||
|
|
||||||
-- List of the bare_jids of all occupants that are currently joining (went through pre-join) and will be promoted
|
-- List of the bare_jids of all occupants that are currently joining (went through pre-join) and will be promoted
|
||||||
-- as moderators. As pre-join (where added) and joined event (where removed) happen one after another this list should
|
-- as moderators. As pre-join (where added) and joined event (where removed) happen one after another this list should
|
||||||
-- have length of 1
|
-- have length of 1
|
||||||
local joining_moderator_participants = {};
|
local joining_moderator_participants = {};
|
||||||
|
|
||||||
|
module:hook("muc-room-created", function(event)
|
||||||
|
local room = event.room;
|
||||||
|
|
||||||
|
if room.jitsiMetadata then
|
||||||
|
room.jitsiMetadata.allownersEnabled = true;
|
||||||
|
end
|
||||||
|
end, -2); -- room_metadata should run before this module on -1
|
||||||
|
|
||||||
module:hook("muc-occupant-pre-join", function (event)
|
module:hook("muc-occupant-pre-join", function (event)
|
||||||
local room, occupant = event.room, event.occupant;
|
local room, occupant = event.room, event.occupant;
|
||||||
|
|
||||||
@ -87,7 +93,7 @@ function filter_stanza(stanza)
|
|||||||
end
|
end
|
||||||
|
|
||||||
-- we want to filter presences only on this host for allowners and skip anything like lobby etc.
|
-- we want to filter presences only on this host for allowners and skip anything like lobby etc.
|
||||||
local host_from = jid_host(stanza.attr.from);
|
local host_from = jid_host(room_jid_match_rewrite(stanza.attr.from));
|
||||||
if host_from ~= module.host then
|
if host_from ~= module.host then
|
||||||
return stanza;
|
return stanza;
|
||||||
end
|
end
|
||||||
|
@ -200,8 +200,14 @@ end
|
|||||||
|
|
||||||
-- Managing breakout rooms
|
-- Managing breakout rooms
|
||||||
|
|
||||||
function create_breakout_room(room_jid, subject)
|
function create_breakout_room(orig_room, subject)
|
||||||
local main_room, main_room_jid = get_main_room(room_jid);
|
local main_room, main_room_jid = get_main_room(orig_room.jid);
|
||||||
|
|
||||||
|
if orig_room ~= main_room then
|
||||||
|
module:log('warn', 'Invalid create breakout room request for %s', orig_room.jid);
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
|
||||||
local breakout_room_jid = uuid_gen() .. '@' .. breakout_rooms_muc_component_config;
|
local breakout_room_jid = uuid_gen() .. '@' .. breakout_rooms_muc_component_config;
|
||||||
|
|
||||||
if not main_room._data.breakout_rooms then
|
if not main_room._data.breakout_rooms then
|
||||||
@ -219,13 +225,18 @@ function create_breakout_room(room_jid, subject)
|
|||||||
broadcast_breakout_rooms(main_room_jid);
|
broadcast_breakout_rooms(main_room_jid);
|
||||||
end
|
end
|
||||||
|
|
||||||
function destroy_breakout_room(room_jid, message)
|
function destroy_breakout_room(orig_room, room_jid, message)
|
||||||
local main_room, main_room_jid = get_main_room(room_jid);
|
local main_room, main_room_jid = get_main_room(room_jid);
|
||||||
|
|
||||||
if room_jid == main_room_jid then
|
if room_jid == main_room_jid then
|
||||||
return;
|
return;
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if orig_room ~= main_room then
|
||||||
|
module:log('warn', 'Invalid destroy breakout room request for %s', orig_room.jid);
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
|
||||||
local breakout_room = breakout_rooms_muc_service.get_room_from_jid(room_jid);
|
local breakout_room = breakout_rooms_muc_service.get_room_from_jid(room_jid);
|
||||||
|
|
||||||
if breakout_room then
|
if breakout_room then
|
||||||
@ -244,13 +255,18 @@ function destroy_breakout_room(room_jid, message)
|
|||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function rename_breakout_room(room_jid, name)
|
function rename_breakout_room(orig_room, room_jid, name)
|
||||||
local main_room, main_room_jid = get_main_room(room_jid);
|
local main_room, main_room_jid = get_main_room(room_jid);
|
||||||
|
|
||||||
if room_jid == main_room_jid then
|
if room_jid == main_room_jid then
|
||||||
return;
|
return;
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if orig_room ~= main_room then
|
||||||
|
module:log('warn', 'Invalid rename breakout room request for %s', orig_room.jid);
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
|
||||||
if main_room then
|
if main_room then
|
||||||
if main_room._data.breakout_rooms then
|
if main_room._data.breakout_rooms then
|
||||||
main_room._data.breakout_rooms[room_jid] = name;
|
main_room._data.breakout_rooms[room_jid] = name;
|
||||||
@ -322,18 +338,25 @@ function on_message(event)
|
|||||||
end
|
end
|
||||||
|
|
||||||
if message.attr.type == JSON_TYPE_ADD_BREAKOUT_ROOM then
|
if message.attr.type == JSON_TYPE_ADD_BREAKOUT_ROOM then
|
||||||
create_breakout_room(room.jid, message.attr.subject);
|
create_breakout_room(room, message.attr.subject);
|
||||||
return true;
|
return true;
|
||||||
elseif message.attr.type == JSON_TYPE_REMOVE_BREAKOUT_ROOM then
|
elseif message.attr.type == JSON_TYPE_REMOVE_BREAKOUT_ROOM then
|
||||||
destroy_breakout_room(message.attr.breakoutRoomJid);
|
destroy_breakout_room(room, message.attr.breakoutRoomJid);
|
||||||
return true;
|
return true;
|
||||||
elseif message.attr.type == JSON_TYPE_RENAME_BREAKOUT_ROOM then
|
elseif message.attr.type == JSON_TYPE_RENAME_BREAKOUT_ROOM then
|
||||||
rename_breakout_room(message.attr.breakoutRoomJid, message.attr.subject);
|
rename_breakout_room(room, message.attr.breakoutRoomJid, message.attr.subject);
|
||||||
return true;
|
return true;
|
||||||
elseif message.attr.type == JSON_TYPE_MOVE_TO_ROOM_REQUEST then
|
elseif message.attr.type == JSON_TYPE_MOVE_TO_ROOM_REQUEST then
|
||||||
local participant_jid = message.attr.participantJid;
|
local participant_jid = message.attr.participantJid;
|
||||||
local target_room_jid = message.attr.roomJid;
|
local target_room_jid = message.attr.roomJid;
|
||||||
|
|
||||||
|
if not room._data.breakout_rooms or not (
|
||||||
|
room._data.breakout_rooms[target_room_jid] or target_room_jid == internal_room_jid_match_rewrite(room.jid))
|
||||||
|
then
|
||||||
|
module:log('warn', 'Invalid breakout room %s for %s', target_room_jid, room.jid);
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
|
||||||
local json_msg, error = json.encode({
|
local json_msg, error = json.encode({
|
||||||
type = BREAKOUT_ROOMS_IDENTITY_TYPE,
|
type = BREAKOUT_ROOMS_IDENTITY_TYPE,
|
||||||
event = JSON_TYPE_MOVE_TO_ROOM_REQUEST,
|
event = JSON_TYPE_MOVE_TO_ROOM_REQUEST,
|
||||||
@ -342,6 +365,7 @@ function on_message(event)
|
|||||||
|
|
||||||
if not json_msg then
|
if not json_msg then
|
||||||
module:log('error', 'skip sending request room:%s error:%s', room.jid, error);
|
module:log('error', 'skip sending request room:%s error:%s', room.jid, error);
|
||||||
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
send_json_msg(participant_jid, json_msg)
|
send_json_msg(participant_jid, json_msg)
|
||||||
@ -416,6 +440,16 @@ function exist_occupants_in_rooms(main_room)
|
|||||||
return false;
|
return false;
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function on_occupant_pre_leave(event)
|
||||||
|
local room, occupant, session, stanza = event.room, event.occupant, event.origin, event.stanza;
|
||||||
|
|
||||||
|
local main_room = get_main_room(room.jid);
|
||||||
|
|
||||||
|
prosody.events.fire_event('jitsi-breakout-occupant-leaving', {
|
||||||
|
room = room; main_room = main_room; occupant = occupant; stanza = stanza; session = session;
|
||||||
|
});
|
||||||
|
end
|
||||||
|
|
||||||
function on_occupant_left(event)
|
function on_occupant_left(event)
|
||||||
local room_jid = event.room.jid;
|
local room_jid = event.room.jid;
|
||||||
|
|
||||||
@ -481,7 +515,7 @@ function on_main_room_destroyed(event)
|
|||||||
end
|
end
|
||||||
|
|
||||||
for breakout_room_jid in pairs(main_room._data.breakout_rooms or {}) do
|
for breakout_room_jid in pairs(main_room._data.breakout_rooms or {}) do
|
||||||
destroy_breakout_room(breakout_room_jid, event.reason)
|
destroy_breakout_room(main_room, breakout_room_jid, event.reason)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -510,6 +544,7 @@ function process_breakout_rooms_muc_loaded(breakout_rooms_muc, host_module)
|
|||||||
host_module:hook('muc-occupant-joined', on_occupant_joined);
|
host_module:hook('muc-occupant-joined', on_occupant_joined);
|
||||||
host_module:hook('muc-occupant-left', on_occupant_left);
|
host_module:hook('muc-occupant-left', on_occupant_left);
|
||||||
host_module:hook('muc-room-pre-create', on_breakout_room_pre_create);
|
host_module:hook('muc-room-pre-create', on_breakout_room_pre_create);
|
||||||
|
host_module:hook('muc-occupant-pre-leave', on_occupant_pre_leave);
|
||||||
|
|
||||||
host_module:hook('muc-disco#info', function (event)
|
host_module:hook('muc-disco#info', function (event)
|
||||||
local room = event.room;
|
local room = event.room;
|
||||||
@ -526,7 +561,7 @@ function process_breakout_rooms_muc_loaded(breakout_rooms_muc, host_module)
|
|||||||
name = 'muc#roominfo_breakout_main_room';
|
name = 'muc#roominfo_breakout_main_room';
|
||||||
label = 'The main room associated with this breakout room';
|
label = 'The main room associated with this breakout room';
|
||||||
});
|
});
|
||||||
event.formdata['muc#roominfo_breakout_main_room'] = main_room_jid;
|
event.formdata['muc#roominfo_breakout_main_room'] = internal_room_jid_match_rewrite(main_room_jid);
|
||||||
|
|
||||||
-- If the main room has a lobby, make it so this breakout room also uses it.
|
-- If the main room has a lobby, make it so this breakout room also uses it.
|
||||||
if (main_room and main_room._data.lobbyroom and main_room:get_members_only()) then
|
if (main_room and main_room._data.lobbyroom and main_room:get_members_only()) then
|
||||||
@ -553,7 +588,7 @@ function process_breakout_rooms_muc_loaded(breakout_rooms_muc, host_module)
|
|||||||
table.insert(event.form, {
|
table.insert(event.form, {
|
||||||
name = 'muc#roominfo_breakout_main_room';
|
name = 'muc#roominfo_breakout_main_room';
|
||||||
label = 'The main room associated with this breakout room';
|
label = 'The main room associated with this breakout room';
|
||||||
value = main_room_jid;
|
value = internal_room_jid_match_rewrite(main_room_jid);
|
||||||
});
|
});
|
||||||
end);
|
end);
|
||||||
|
|
||||||
|
@ -4,9 +4,9 @@
|
|||||||
-- Copyright (C) 2023-present 8x8, Inc.
|
-- Copyright (C) 2023-present 8x8, Inc.
|
||||||
|
|
||||||
local oss_util = module:require "util";
|
local oss_util = module:require "util";
|
||||||
|
local is_admin = oss_util.is_admin;
|
||||||
local is_healthcheck_room = oss_util.is_healthcheck_room;
|
local is_healthcheck_room = oss_util.is_healthcheck_room;
|
||||||
local process_host_module = oss_util.process_host_module;
|
local process_host_module = oss_util.process_host_module;
|
||||||
local um_is_admin = require "core.usermanager".is_admin;
|
|
||||||
local inspect = require('inspect');
|
local inspect = require('inspect');
|
||||||
local jid_bare = require "util.jid".bare;
|
local jid_bare = require "util.jid".bare;
|
||||||
local jid = require "util.jid";
|
local jid = require "util.jid";
|
||||||
@ -21,10 +21,6 @@ if lobby_muc_component_config == nil then
|
|||||||
return ;
|
return ;
|
||||||
end
|
end
|
||||||
|
|
||||||
local function is_admin(occupant_jid)
|
|
||||||
return um_is_admin(occupant_jid, module.host);
|
|
||||||
end
|
|
||||||
|
|
||||||
local function remove_flip_tag(stanza)
|
local function remove_flip_tag(stanza)
|
||||||
stanza:maptags(function(tag)
|
stanza:maptags(function(tag)
|
||||||
if tag and tag.name == "flip_device" then
|
if tag and tag.name == "flip_device" then
|
||||||
|
@ -1,6 +1,30 @@
|
|||||||
-- This module makes all MUCs in Prosody unavailable on disco#items query
|
-- This module makes all MUCs in Prosody unavailable on disco#items query
|
||||||
-- Copyright (C) 2023-present 8x8, Inc.
|
-- Copyright (C) 2023-present 8x8, Inc.
|
||||||
|
local jid = require 'util.jid';
|
||||||
|
local st = require 'util.stanza';
|
||||||
|
|
||||||
module:hook("muc-room-pre-create", function(event)
|
local util = module:require 'util';
|
||||||
|
local get_room_from_jid = util.get_room_from_jid;
|
||||||
|
|
||||||
|
module:hook('muc-room-pre-create', function(event)
|
||||||
event.room:set_hidden(true);
|
event.room:set_hidden(true);
|
||||||
end, -1);
|
end, -1);
|
||||||
|
|
||||||
|
for _, event_name in pairs {
|
||||||
|
'iq-get/bare/http://jabber.org/protocol/disco#info:query';
|
||||||
|
'iq-get/host/http://jabber.org/protocol/disco#info:query';
|
||||||
|
} do
|
||||||
|
module:hook(event_name, function (event)
|
||||||
|
local origin, stanza = event.origin, event.stanza;
|
||||||
|
local room_jid = jid.bare(stanza.attr.to);
|
||||||
|
local room = get_room_from_jid(room_jid);
|
||||||
|
|
||||||
|
if room then
|
||||||
|
if not room:get_occupant_by_real_jid(stanza.attr.from) then
|
||||||
|
origin.send(st.error_reply(stanza, 'auth', 'forbidden'));
|
||||||
|
return true;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- prosody will send item-not-found
|
||||||
|
end, 1) -- make sure we handle it before prosody that uses priority -2 for this
|
||||||
|
end
|
||||||
|
171
roles/jitsi/files/prosody/modules/mod_muc_kick_participant.lua
Normal file
171
roles/jitsi/files/prosody/modules/mod_muc_kick_participant.lua
Normal file
@ -0,0 +1,171 @@
|
|||||||
|
-- http endpoint to kick participants, access is based on provided jwt token
|
||||||
|
-- the correct jigasi we fined based on the display name and the number provided
|
||||||
|
-- Copyright (C) 2023-present 8x8, Inc.
|
||||||
|
|
||||||
|
local util = module:require "util";
|
||||||
|
local async_handler_wrapper = util.async_handler_wrapper;
|
||||||
|
local is_sip_jigasi = util.is_sip_jigasi;
|
||||||
|
local starts_with = util.starts_with;
|
||||||
|
local formdecode = require "util.http".formdecode;
|
||||||
|
local urlencode = require "util.http".urlencode;
|
||||||
|
local jid = require "util.jid";
|
||||||
|
local json = require 'cjson.safe';
|
||||||
|
|
||||||
|
local muc_domain_prefix = module:get_option_string("muc_mapper_domain_prefix", "conference");
|
||||||
|
|
||||||
|
local muc_domain_base = module:get_option_string("muc_mapper_domain_base");
|
||||||
|
if not muc_domain_base then
|
||||||
|
module:log("warn", "No 'muc_domain_base' option set, disabling kick check endpoint.");
|
||||||
|
return ;
|
||||||
|
end
|
||||||
|
|
||||||
|
local json_content_type = "application/json";
|
||||||
|
|
||||||
|
local token_util = module:require "token/util".new(module);
|
||||||
|
|
||||||
|
local asapKeyServer = module:get_option_string('prosody_password_public_key_repo_url', '');
|
||||||
|
if asapKeyServer == '' then
|
||||||
|
module:log('warn', 'No "prosody_password_public_key_repo_url" option set, disabling kick endpoint.');
|
||||||
|
return ;
|
||||||
|
end
|
||||||
|
|
||||||
|
token_util:set_asap_key_server(asapKeyServer);
|
||||||
|
|
||||||
|
--- Verifies the token
|
||||||
|
-- @param token the token we received
|
||||||
|
-- @param room_address the full room address jid
|
||||||
|
-- @return true if values are ok or false otherwise
|
||||||
|
function verify_token(token, room_address)
|
||||||
|
|
||||||
|
if token == nil then
|
||||||
|
module:log("warn", "no token provided for %s", room_address);
|
||||||
|
return false;
|
||||||
|
end
|
||||||
|
|
||||||
|
local session = {};
|
||||||
|
session.auth_token = token;
|
||||||
|
local verified, reason, msg = token_util:process_and_verify_token(session);
|
||||||
|
if not verified then
|
||||||
|
module:log("warn", "not a valid token %s %s for %s", tostring(reason), tostring(msg), room_address);
|
||||||
|
return false;
|
||||||
|
end
|
||||||
|
|
||||||
|
return true;
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
-- Validates the request by checking for required url param room and
|
||||||
|
-- validates the token provided with the request
|
||||||
|
-- @param request - The request to validate.
|
||||||
|
-- @return [error_code, room]
|
||||||
|
local function validate_and_get_room(request)
|
||||||
|
if not request.url.query then
|
||||||
|
module:log("warn", "No query");
|
||||||
|
return 400, nil;
|
||||||
|
end
|
||||||
|
|
||||||
|
local params = formdecode(request.url.query);
|
||||||
|
local room_name = urlencode(params.room) or "";
|
||||||
|
local subdomain = urlencode(params.prefix) or "";
|
||||||
|
|
||||||
|
if not room_name then
|
||||||
|
module:log("warn", "Missing room param for %s", room_name);
|
||||||
|
return 400, nil;
|
||||||
|
end
|
||||||
|
|
||||||
|
local room_address = jid.join(room_name, muc_domain_prefix.."."..muc_domain_base);
|
||||||
|
|
||||||
|
if subdomain and subdomain ~= "" then
|
||||||
|
room_address = "["..subdomain.."]"..room_address;
|
||||||
|
end
|
||||||
|
|
||||||
|
-- verify access
|
||||||
|
local token = request.headers["authorization"]
|
||||||
|
|
||||||
|
if token and starts_with(token,'Bearer ') then
|
||||||
|
token = token:sub(8,#token)
|
||||||
|
end
|
||||||
|
|
||||||
|
if not verify_token(token, room_address) then
|
||||||
|
return 403, nil;
|
||||||
|
end
|
||||||
|
|
||||||
|
local room = get_room_from_jid(room_address);
|
||||||
|
|
||||||
|
if not room then
|
||||||
|
module:log("warn", "No room found for %s", room_address);
|
||||||
|
return 404, nil;
|
||||||
|
else
|
||||||
|
return 200, room;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function handle_kick_participant (event)
|
||||||
|
local request = event.request;
|
||||||
|
if request.headers.content_type ~= json_content_type
|
||||||
|
or (not request.body or #request.body == 0) then
|
||||||
|
module:log("warn", "Wrong content type: %s", request.headers.content_type);
|
||||||
|
return { status_code = 400; }
|
||||||
|
end
|
||||||
|
|
||||||
|
local params, error = json.decode(request.body);
|
||||||
|
if not params then
|
||||||
|
module:log("warn", "Missing params error:%s", error);
|
||||||
|
return { status_code = 400; }
|
||||||
|
end
|
||||||
|
|
||||||
|
local number = params["number"];
|
||||||
|
local participantId = params["participantId"];
|
||||||
|
|
||||||
|
if (not number and not participantId) or (number and participantId) then
|
||||||
|
module:log("warn", "Invalid parameters: exactly one of 'number' or 'participantId' must be provided.");
|
||||||
|
return { status_code = 400; };
|
||||||
|
end
|
||||||
|
|
||||||
|
local error_code, room = validate_and_get_room(request);
|
||||||
|
|
||||||
|
if error_code and error_code ~= 200 then
|
||||||
|
module:log("error", "Error validating %s", error_code);
|
||||||
|
return { error_code = 400; }
|
||||||
|
end
|
||||||
|
|
||||||
|
if not room then
|
||||||
|
return { status_code = 404; }
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, occupant in room:each_occupant() do
|
||||||
|
local pr = occupant:get_presence();
|
||||||
|
|
||||||
|
if is_participant_match(pr, number, participantId) then
|
||||||
|
room:set_role(true, occupant.nick, nil);
|
||||||
|
module:log('info', 'Occupant kicked %s from %s', occupant.nick, room.jid);
|
||||||
|
return { status_code = 200; }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- not found participant to kick
|
||||||
|
return { status_code = 404; };
|
||||||
|
end
|
||||||
|
|
||||||
|
function is_participant_match(pr, number, participantId)
|
||||||
|
if number then
|
||||||
|
local displayName = pr:get_child_text('nick', 'http://jabber.org/protocol/nick');
|
||||||
|
return is_sip_jigasi(pr) and displayName and starts_with(displayName, number);
|
||||||
|
elseif participantId then
|
||||||
|
local from = pr.attr.from;
|
||||||
|
local _, _, from_resource = jid.split(from);
|
||||||
|
if from_resource then
|
||||||
|
return from_resource == participantId;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return false;
|
||||||
|
end
|
||||||
|
|
||||||
|
module:log("info","Adding http handler for /kick-participant on %s", module.host);
|
||||||
|
module:depends("http");
|
||||||
|
module:provides("http", {
|
||||||
|
default_path = "/";
|
||||||
|
route = {
|
||||||
|
["PUT kick-participant"] = function (event) return async_handler_wrapper(event, handle_kick_participant) end;
|
||||||
|
};
|
||||||
|
});
|
@ -193,6 +193,10 @@ function filter_stanza(stanza)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if not from_real_jid then
|
||||||
|
return nil;
|
||||||
|
end
|
||||||
|
|
||||||
local is_from_moderator = lobby_room:get_affiliation(from_real_jid) == 'owner';
|
local is_from_moderator = lobby_room:get_affiliation(from_real_jid) == 'owner';
|
||||||
|
|
||||||
if is_to_moderator or is_from_moderator then
|
if is_to_moderator or is_from_moderator then
|
||||||
|
@ -1,21 +1,29 @@
|
|||||||
|
local jid = require 'util.jid';
|
||||||
|
local json = require 'cjson.safe';
|
||||||
local queue = require "util.queue";
|
local queue = require "util.queue";
|
||||||
local uuid_gen = require "util.uuid".generate;
|
local uuid_gen = require "util.uuid".generate;
|
||||||
local main_util = module:require "util";
|
local main_util = module:require "util";
|
||||||
|
local is_admin = main_util.is_admin;
|
||||||
local ends_with = main_util.ends_with;
|
local ends_with = main_util.ends_with;
|
||||||
|
local get_room_from_jid = main_util.get_room_from_jid;
|
||||||
local is_healthcheck_room = main_util.is_healthcheck_room;
|
local is_healthcheck_room = main_util.is_healthcheck_room;
|
||||||
local internal_room_jid_match_rewrite = main_util.internal_room_jid_match_rewrite;
|
local internal_room_jid_match_rewrite = main_util.internal_room_jid_match_rewrite;
|
||||||
local presence_check_status = main_util.presence_check_status;
|
local presence_check_status = main_util.presence_check_status;
|
||||||
|
local extract_subdomain = main_util.extract_subdomain;
|
||||||
local um_is_admin = require 'core.usermanager'.is_admin;
|
|
||||||
local function is_admin(jid)
|
|
||||||
return um_is_admin(jid, module.host);
|
|
||||||
end
|
|
||||||
|
|
||||||
local QUEUE_MAX_SIZE = 500;
|
local QUEUE_MAX_SIZE = 500;
|
||||||
|
|
||||||
-- Module that generates a unique meetingId, attaches it to the room
|
module:depends("jitsi_permissions");
|
||||||
-- and adds it to all disco info form data (when room is queried or in the
|
|
||||||
-- initial room owner config)
|
-- Common module for all logic that can be loaded under the conference muc component.
|
||||||
|
--
|
||||||
|
-- This module:
|
||||||
|
-- a) Generates a unique meetingId, attaches it to the room and adds it to all disco info form data
|
||||||
|
-- (when room is queried or in the initial room owner config).
|
||||||
|
-- b) Updates user region (obtain it from the incoming http headers) in the occupant's presence on pre-join.
|
||||||
|
-- c) Avoids any participant joining the room in the interval between creating the room and jicofo entering the room.
|
||||||
|
-- d) Removes any nick that maybe set to messages being sent to the room.
|
||||||
|
-- e) Fires event for received endpoint messages (optimization to decode them once).
|
||||||
|
|
||||||
-- Hook to assign meetingId for new rooms
|
-- Hook to assign meetingId for new rooms
|
||||||
module:hook("muc-room-created", function(event)
|
module:hook("muc-room-created", function(event)
|
||||||
@ -75,17 +83,35 @@ module:hook('muc-broadcast-presence', function (event)
|
|||||||
end
|
end
|
||||||
end);
|
end);
|
||||||
|
|
||||||
--- Avoids any participant joining the room in the interval between creating the room
|
local function process_region(session, stanza)
|
||||||
--- and jicofo entering the room
|
if not session.user_region then
|
||||||
module:hook('muc-occupant-pre-join', function (event)
|
return;
|
||||||
local room, stanza = event.room, event.stanza;
|
end
|
||||||
|
|
||||||
-- we skip processing only if jicofo_lock is set to false
|
local region = stanza:get_child_text('jitsi_participant_region');
|
||||||
if room._data.jicofo_lock == false or is_healthcheck_room(room.jid) then
|
if region then
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
|
||||||
|
stanza:tag('jitsi_participant_region'):text(session.user_region):up();
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Avoids any participant joining the room in the interval between creating the room
|
||||||
|
--- and jicofo entering the room
|
||||||
|
module:hook('muc-occupant-pre-join', function (event)
|
||||||
|
local occupant, room, stanza = event.occupant, event.room, event.stanza;
|
||||||
|
|
||||||
|
local is_health_room = is_healthcheck_room(room.jid);
|
||||||
|
-- check for region
|
||||||
|
if not is_admin(occupant.bare_jid) and not is_health_room then
|
||||||
|
process_region(event.origin, stanza);
|
||||||
|
end
|
||||||
|
|
||||||
|
-- we skip processing only if jicofo_lock is set to false
|
||||||
|
if room._data.jicofo_lock == false or is_health_room then
|
||||||
return;
|
return;
|
||||||
end
|
end
|
||||||
|
|
||||||
local occupant = event.occupant;
|
|
||||||
if ends_with(occupant.nick, '/focus') then
|
if ends_with(occupant.nick, '/focus') then
|
||||||
module:fire_event('jicofo-unlock-room', { room = room; });
|
module:fire_event('jicofo-unlock-room', { room = room; });
|
||||||
else
|
else
|
||||||
@ -131,3 +157,86 @@ module:hook('jicofo-unlock-room', handle_jicofo_unlock);
|
|||||||
module:hook("muc-occupant-groupchat", function(event)
|
module:hook("muc-occupant-groupchat", function(event)
|
||||||
event.stanza:remove_children('nick', 'http://jabber.org/protocol/nick');
|
event.stanza:remove_children('nick', 'http://jabber.org/protocol/nick');
|
||||||
end, 45); -- prosody check is prio 50, we want to run after it
|
end, 45); -- prosody check is prio 50, we want to run after it
|
||||||
|
|
||||||
|
module:hook('message/bare', function(event)
|
||||||
|
local stanza = event.stanza;
|
||||||
|
|
||||||
|
if stanza.attr.type ~= 'groupchat' then
|
||||||
|
return nil;
|
||||||
|
end
|
||||||
|
|
||||||
|
-- we are interested in all messages without a body
|
||||||
|
local body = stanza:get_child('body')
|
||||||
|
if body then
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
|
||||||
|
local room = get_room_from_jid(stanza.attr.to);
|
||||||
|
if not room then
|
||||||
|
module:log('warn', 'No room found found for %s', stanza.attr.to);
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
|
||||||
|
local occupant_jid = stanza.attr.from;
|
||||||
|
local occupant = room:get_occupant_by_real_jid(occupant_jid);
|
||||||
|
if not occupant then
|
||||||
|
module:log("error", "Occupant sending msg %s was not found in room %s", occupant_jid, room.jid)
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
|
||||||
|
local json_message = stanza:get_child_text('json-message', 'http://jitsi.org/jitmeet');
|
||||||
|
if not json_message then
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
|
||||||
|
-- TODO: add optimization by moving type and certain fields like is_interim as attribute on 'json-message'
|
||||||
|
-- using string find is roughly 70x faster than json decode for checking the value
|
||||||
|
if string.find(json_message, '"is_interim":true', 1, true) then
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
|
||||||
|
local msg_obj, error = json.decode(json_message);
|
||||||
|
|
||||||
|
if error then
|
||||||
|
module:log('error', 'Error decoding data error:%s Sender: %s to:%s', error, stanza.attr.from, stanza.attr.to);
|
||||||
|
return true;
|
||||||
|
end
|
||||||
|
|
||||||
|
if msg_obj.transcript ~= nil then
|
||||||
|
local transcription = msg_obj;
|
||||||
|
|
||||||
|
-- in case of the string matching optimization above failed
|
||||||
|
if transcription.is_interim then
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
|
||||||
|
-- TODO what if we have multiple alternative transcriptions not just 1
|
||||||
|
local text_message = transcription.transcript[1].text;
|
||||||
|
--do not send empty messages
|
||||||
|
if text_message == '' then
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
|
||||||
|
local user_id = transcription.participant.id;
|
||||||
|
local who = room:get_occupant_by_nick(jid.bare(room.jid)..'/'..user_id);
|
||||||
|
|
||||||
|
transcription.jid = who and who.jid;
|
||||||
|
transcription.session_id = room._data.meetingId;
|
||||||
|
|
||||||
|
local tenant, conference_name, id = extract_subdomain(jid.node(room.jid));
|
||||||
|
if tenant then
|
||||||
|
transcription.fqn = tenant..'/'..conference_name;
|
||||||
|
else
|
||||||
|
transcription.fqn = conference_name;
|
||||||
|
end
|
||||||
|
transcription.customer_id = id;
|
||||||
|
|
||||||
|
return module:fire_event('jitsi-transcript-received', {
|
||||||
|
room = room, occupant = occupant, transcription = transcription, stanza = stanza });
|
||||||
|
end
|
||||||
|
|
||||||
|
return module:fire_event('jitsi-endpoint-message-received', {
|
||||||
|
room = room, occupant = occupant, message = msg_obj,
|
||||||
|
origin = event.origin,
|
||||||
|
stanza = stanza, raw_message = json_message });
|
||||||
|
end);
|
||||||
|
@ -6,9 +6,9 @@
|
|||||||
-- the guest domain which is anonymous.
|
-- the guest domain which is anonymous.
|
||||||
-- The module has the option to set participants to moderators when connected via token/when they are authenticated
|
-- The module has the option to set participants to moderators when connected via token/when they are authenticated
|
||||||
-- This module depends on mod_persistent_lobby.
|
-- This module depends on mod_persistent_lobby.
|
||||||
local um_is_admin = require 'core.usermanager'.is_admin;
|
|
||||||
local jid = require 'util.jid';
|
local jid = require 'util.jid';
|
||||||
local util = module:require "util";
|
local util = module:require "util";
|
||||||
|
local is_admin = util.is_admin;
|
||||||
local is_healthcheck_room = util.is_healthcheck_room;
|
local is_healthcheck_room = util.is_healthcheck_room;
|
||||||
local is_moderated = util.is_moderated;
|
local is_moderated = util.is_moderated;
|
||||||
local process_host_module = util.process_host_module;
|
local process_host_module = util.process_host_module;
|
||||||
@ -43,10 +43,6 @@ if not disable_auto_owners then
|
|||||||
end, 2);
|
end, 2);
|
||||||
end
|
end
|
||||||
|
|
||||||
local function is_admin(jid)
|
|
||||||
return um_is_admin(jid, module.host);
|
|
||||||
end
|
|
||||||
|
|
||||||
-- if not authenticated user is trying to join the room we enable lobby in it
|
-- if not authenticated user is trying to join the room we enable lobby in it
|
||||||
-- and wait for the moderator to join
|
-- and wait for the moderator to join
|
||||||
module:hook('muc-occupant-pre-join', function (event)
|
module:hook('muc-occupant-pre-join', function (event)
|
||||||
|
@ -11,26 +11,8 @@ local muc = module:depends("muc");
|
|||||||
local NS_NICK = 'http://jabber.org/protocol/nick';
|
local NS_NICK = 'http://jabber.org/protocol/nick';
|
||||||
local is_healthcheck_room = util.is_healthcheck_room;
|
local is_healthcheck_room = util.is_healthcheck_room;
|
||||||
|
|
||||||
-- Checks if the given stanza contains a JSON message,
|
local POLLS_LIMIT = 128;
|
||||||
-- and that the message type pertains to the polls feature.
|
local POLL_PAYLOAD_LIMIT = 1024;
|
||||||
-- If yes, returns the parsed message. Otherwise, returns nil.
|
|
||||||
local function get_poll_message(stanza)
|
|
||||||
if stanza.attr.type ~= "groupchat" then
|
|
||||||
return nil;
|
|
||||||
end
|
|
||||||
local json_data = stanza:get_child_text("json-message", "http://jitsi.org/jitmeet");
|
|
||||||
if json_data == nil then
|
|
||||||
return nil;
|
|
||||||
end
|
|
||||||
local data, error = json.decode(json_data);
|
|
||||||
if not data or (data.type ~= "new-poll" and data.type ~= "answer-poll") then
|
|
||||||
if error then
|
|
||||||
module:log('error', 'Error decoding data error:%s', error);
|
|
||||||
end
|
|
||||||
return nil;
|
|
||||||
end
|
|
||||||
return data;
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Logs a warning and returns true if a room does not
|
-- Logs a warning and returns true if a room does not
|
||||||
-- have poll data associated with it.
|
-- have poll data associated with it.
|
||||||
@ -72,6 +54,7 @@ module:hook("muc-room-created", function(event)
|
|||||||
room.polls = {
|
room.polls = {
|
||||||
by_id = {};
|
by_id = {};
|
||||||
order = {};
|
order = {};
|
||||||
|
count = 0;
|
||||||
};
|
};
|
||||||
end);
|
end);
|
||||||
|
|
||||||
@ -79,27 +62,46 @@ end);
|
|||||||
-- by listening to "new-poll" and "answer-poll" messages,
|
-- by listening to "new-poll" and "answer-poll" messages,
|
||||||
-- and updating the room poll data accordingly.
|
-- and updating the room poll data accordingly.
|
||||||
-- This mirrors the client-side poll update logic.
|
-- This mirrors the client-side poll update logic.
|
||||||
module:hook("message/bare", function(event)
|
module:hook('jitsi-endpoint-message-received', function(event)
|
||||||
local data = get_poll_message(event.stanza);
|
local data, error, occupant, room, origin, stanza
|
||||||
if data == nil then return end
|
= event.message, event.error, event.occupant, event.room, event.origin, event.stanza;
|
||||||
|
|
||||||
local room = muc.get_room_from_jid(event.stanza.attr.to);
|
if not data or (data.type ~= "new-poll" and data.type ~= "answer-poll") then
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
|
||||||
|
if string.len(event.raw_message) >= POLL_PAYLOAD_LIMIT then
|
||||||
|
module:log('error', 'Poll payload too large, discarding. Sender: %s to:%s', stanza.attr.from, stanza.attr.to);
|
||||||
|
return true;
|
||||||
|
end
|
||||||
|
|
||||||
if data.type == "new-poll" then
|
if data.type == "new-poll" then
|
||||||
if check_polls(room) then return end
|
if check_polls(room) then return end
|
||||||
|
|
||||||
local occupant_jid = event.stanza.attr.from;
|
|
||||||
local occupant = room:get_occupant_by_real_jid(occupant_jid);
|
|
||||||
if not occupant then
|
|
||||||
module:log("error", "Occupant %s was not found in room %s", occupant_jid, room.jid)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
local poll_creator = get_occupant_details(occupant)
|
local poll_creator = get_occupant_details(occupant)
|
||||||
if not poll_creator then
|
if not poll_creator then
|
||||||
module:log("error", "Cannot retrieve poll creator id and name for %s from %s", occupant.jid, room.jid)
|
module:log("error", "Cannot retrieve poll creator id and name for %s from %s", occupant.jid, room.jid)
|
||||||
return
|
return
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if room.polls.count >= POLLS_LIMIT then
|
||||||
|
module:log("error", "Too many polls created in %s", room.jid)
|
||||||
|
return true;
|
||||||
|
end
|
||||||
|
|
||||||
|
if room.polls.by_id[data.pollId] ~= nil then
|
||||||
|
module:log("error", "Poll already exists: %s", data.pollId);
|
||||||
|
origin.send(st.error_reply(stanza, 'cancel', 'not-allowed', 'Poll already exists'));
|
||||||
|
return true;
|
||||||
|
end
|
||||||
|
|
||||||
|
if room.jitsiMetadata and room.jitsiMetadata.permissions
|
||||||
|
and room.jitsiMetadata.permissions.pollCreationRestricted
|
||||||
|
and not is_feature_allowed('create-polls', origin.jitsi_meet_context_features) then
|
||||||
|
origin.send(st.error_reply(stanza, 'cancel', 'not-allowed', 'Creation of polls not allowed for user'));
|
||||||
|
return true;
|
||||||
|
end
|
||||||
|
|
||||||
local answers = {}
|
local answers = {}
|
||||||
local compact_answers = {}
|
local compact_answers = {}
|
||||||
for i, name in ipairs(data.answers) do
|
for i, name in ipairs(data.answers) do
|
||||||
@ -117,6 +119,7 @@ module:hook("message/bare", function(event)
|
|||||||
|
|
||||||
room.polls.by_id[data.pollId] = poll
|
room.polls.by_id[data.pollId] = poll
|
||||||
table.insert(room.polls.order, poll)
|
table.insert(room.polls.order, poll)
|
||||||
|
room.polls.count = room.polls.count + 1;
|
||||||
|
|
||||||
local pollData = {
|
local pollData = {
|
||||||
event = event,
|
event = event,
|
||||||
@ -130,16 +133,9 @@ module:hook("message/bare", function(event)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
module:fire_event("poll-created", pollData);
|
module:fire_event("poll-created", pollData);
|
||||||
|
|
||||||
elseif data.type == "answer-poll" then
|
elseif data.type == "answer-poll" then
|
||||||
if check_polls(room) then return end
|
if check_polls(room) then return end
|
||||||
|
|
||||||
local occupant_jid = event.stanza.attr.from;
|
|
||||||
local occupant = room:get_occupant_by_real_jid(occupant_jid);
|
|
||||||
if not occupant then
|
|
||||||
module:log("error", "Occupant %s does not exists for room %s", occupant_jid, room.jid)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
local poll = room.polls.by_id[data.pollId];
|
local poll = room.polls.by_id[data.pollId];
|
||||||
if poll == nil then
|
if poll == nil then
|
||||||
module:log("warn", "answering inexistent poll");
|
module:log("warn", "answering inexistent poll");
|
||||||
|
@ -5,16 +5,13 @@ local update_presence_identity = module:require "util".update_presence_identity;
|
|||||||
-- values are set in the session, then insert them into the presence messages
|
-- values are set in the session, then insert them into the presence messages
|
||||||
-- for that session.
|
-- for that session.
|
||||||
function on_message(event)
|
function on_message(event)
|
||||||
if event and event["stanza"] then
|
local stanza, session = event.stanza, event.origin;
|
||||||
if event.origin and event.origin.jitsi_meet_context_user then
|
if stanza and session then
|
||||||
|
|
||||||
update_presence_identity(
|
update_presence_identity(
|
||||||
event.stanza,
|
stanza,
|
||||||
event.origin.jitsi_meet_context_user,
|
session.jitsi_meet_context_user,
|
||||||
event.origin.jitsi_meet_context_group
|
session.jitsi_meet_context_group
|
||||||
);
|
);
|
||||||
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@ local ip_util = require "util.ip";
|
|||||||
local new_ip = ip_util.new_ip;
|
local new_ip = ip_util.new_ip;
|
||||||
local match_ip = ip_util.match;
|
local match_ip = ip_util.match;
|
||||||
local parse_cidr = ip_util.parse_cidr;
|
local parse_cidr = ip_util.parse_cidr;
|
||||||
|
local get_ip = module:require "util".get_ip;
|
||||||
|
|
||||||
local config = {};
|
local config = {};
|
||||||
local limits_resolution = 1;
|
local limits_resolution = 1;
|
||||||
@ -76,14 +77,6 @@ local function is_whitelisted_host(h)
|
|||||||
return config.whitelist_hosts:contains(h);
|
return config.whitelist_hosts:contains(h);
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Discover real remote IP of a session
|
|
||||||
-- Note: http_server.get_request_from_conn() was added in Prosody 0.12.3,
|
|
||||||
-- this code provides backwards compatibility with older versions
|
|
||||||
local get_request_from_conn = http_server.get_request_from_conn or function (conn)
|
|
||||||
local response = conn and conn._http_open_response;
|
|
||||||
return response and response.request or nil;
|
|
||||||
end;
|
|
||||||
|
|
||||||
-- Add an IP to the set of limied IPs
|
-- Add an IP to the set of limied IPs
|
||||||
local function limit_ip(ip)
|
local function limit_ip(ip)
|
||||||
module:log("info", "Limiting %s due to login/join rate exceeded.", ip);
|
module:log("info", "Limiting %s due to login/join rate exceeded.", ip);
|
||||||
@ -192,9 +185,8 @@ local function filter_hook(session)
|
|||||||
return;
|
return;
|
||||||
end
|
end
|
||||||
|
|
||||||
local request = get_request_from_conn(session.conn);
|
local ip = get_ip(session);
|
||||||
local ip = request and request.ip or session.ip;
|
module:log("debug", "New session from %s", ip);
|
||||||
module:log("debug", "New session from %s", ip);
|
|
||||||
if is_whitelisted(ip) or is_whitelisted_host(session.host) then
|
if is_whitelisted(ip) or is_whitelisted_host(session.host) then
|
||||||
return;
|
return;
|
||||||
end
|
end
|
||||||
|
@ -10,25 +10,36 @@
|
|||||||
-- Component "metadata.jitmeet.example.com" "room_metadata_component"
|
-- Component "metadata.jitmeet.example.com" "room_metadata_component"
|
||||||
-- muc_component = "conference.jitmeet.example.com"
|
-- muc_component = "conference.jitmeet.example.com"
|
||||||
-- breakout_rooms_component = "breakout.jitmeet.example.com"
|
-- breakout_rooms_component = "breakout.jitmeet.example.com"
|
||||||
|
local filters = require 'util.filters';
|
||||||
local jid_node = require 'util.jid'.node;
|
local jid_node = require 'util.jid'.node;
|
||||||
local json = require 'cjson.safe';
|
local json = require 'cjson.safe';
|
||||||
local st = require 'util.stanza';
|
local st = require 'util.stanza';
|
||||||
|
local jid = require 'util.jid';
|
||||||
|
|
||||||
local util = module:require 'util';
|
local util = module:require 'util';
|
||||||
|
local is_admin = util.is_admin;
|
||||||
local is_healthcheck_room = util.is_healthcheck_room;
|
local is_healthcheck_room = util.is_healthcheck_room;
|
||||||
local get_room_from_jid = util.get_room_from_jid;
|
local get_room_from_jid = util.get_room_from_jid;
|
||||||
local room_jid_match_rewrite = util.room_jid_match_rewrite;
|
local room_jid_match_rewrite = util.room_jid_match_rewrite;
|
||||||
local internal_room_jid_match_rewrite = util.internal_room_jid_match_rewrite;
|
local internal_room_jid_match_rewrite = util.internal_room_jid_match_rewrite;
|
||||||
local process_host_module = util.process_host_module;
|
local process_host_module = util.process_host_module;
|
||||||
|
local table_shallow_copy = util.table_shallow_copy;
|
||||||
|
local table_add = util.table_add;
|
||||||
|
|
||||||
|
local MUC_NS = 'http://jabber.org/protocol/muc';
|
||||||
local COMPONENT_IDENTITY_TYPE = 'room_metadata';
|
local COMPONENT_IDENTITY_TYPE = 'room_metadata';
|
||||||
local FORM_KEY = 'muc#roominfo_jitsimetadata';
|
local FORM_KEY = 'muc#roominfo_jitsimetadata';
|
||||||
|
|
||||||
local muc_component_host = module:get_option_string('muc_component');
|
local muc_component_host = module:get_option_string('muc_component');
|
||||||
|
|
||||||
if muc_component_host == nil then
|
if muc_component_host == nil then
|
||||||
module:log("error", "No muc_component specified. No muc to operate on!");
|
module:log('error', 'No muc_component specified. No muc to operate on!');
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
|
||||||
|
local muc_domain_base = module:get_option_string('muc_mapper_domain_base');
|
||||||
|
if not muc_domain_base then
|
||||||
|
module:log('warn', 'No muc_domain_base option set.');
|
||||||
return;
|
return;
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -40,10 +51,13 @@ local main_muc_module;
|
|||||||
|
|
||||||
-- Utility functions
|
-- Utility functions
|
||||||
|
|
||||||
function getMetadataJSON(room)
|
-- Returns json string with the metadata for the room.
|
||||||
|
-- @param room The room object.
|
||||||
|
-- @param metadata Optional metadata to use instead of the room's jitsiMetadata.
|
||||||
|
function getMetadataJSON(room, metadata)
|
||||||
local res, error = json.encode({
|
local res, error = json.encode({
|
||||||
type = COMPONENT_IDENTITY_TYPE,
|
type = COMPONENT_IDENTITY_TYPE,
|
||||||
metadata = room.jitsiMetadata or {}
|
metadata = metadata or room.jitsiMetadata or {}
|
||||||
});
|
});
|
||||||
|
|
||||||
if not res then
|
if not res then
|
||||||
@ -53,28 +67,52 @@ function getMetadataJSON(room)
|
|||||||
return res;
|
return res;
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Putting the information on the config form / disco-info allows us to save
|
|
||||||
-- an extra message to users who join later.
|
|
||||||
function getFormData(room)
|
|
||||||
return {
|
|
||||||
name = FORM_KEY;
|
|
||||||
type = 'text-multi';
|
|
||||||
label = 'Room metadata';
|
|
||||||
value = getMetadataJSON(room);
|
|
||||||
};
|
|
||||||
end
|
|
||||||
|
|
||||||
function broadcastMetadata(room)
|
function broadcastMetadata(room)
|
||||||
local json_msg = getMetadataJSON(room);
|
local json_msg = getMetadataJSON(room);
|
||||||
|
|
||||||
|
if not json_msg then
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
|
||||||
for _, occupant in room:each_occupant() do
|
for _, occupant in room:each_occupant() do
|
||||||
send_json_msg(occupant.jid, internal_room_jid_match_rewrite(room.jid), json_msg)
|
send_metadata(occupant, room, json_msg)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
function send_json_msg(to_jid, room_jid, json_msg)
|
function send_metadata(occupant, room, json_msg)
|
||||||
local stanza = st.message({ from = module.host; to = to_jid; })
|
if not json_msg or is_admin(occupant.bare_jid) then
|
||||||
:tag('json-message', { xmlns = 'http://jitsi.org/jitmeet', room = room_jid }):text(json_msg):up();
|
local metadata_to_send = room.jitsiMetadata or {};
|
||||||
|
|
||||||
|
-- we want to send the main meeting participants only to jicofo
|
||||||
|
if is_admin(occupant.bare_jid) then
|
||||||
|
local participants = {};
|
||||||
|
|
||||||
|
if room._data.mainMeetingParticipants then
|
||||||
|
table_add(participants, room._data.mainMeetingParticipants);
|
||||||
|
end
|
||||||
|
|
||||||
|
if room._data.moderator_id then
|
||||||
|
table.insert(participants, room._data.moderator_id);
|
||||||
|
end
|
||||||
|
|
||||||
|
if room._data.moderators then
|
||||||
|
table_add(participants, room._data.moderators);
|
||||||
|
end
|
||||||
|
|
||||||
|
if #participants > 0 then
|
||||||
|
metadata_to_send = table_shallow_copy(metadata_to_send);
|
||||||
|
metadata_to_send.mainMeetingParticipants = participants;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
json_msg = getMetadataJSON(room, metadata_to_send);
|
||||||
|
end
|
||||||
|
|
||||||
|
local stanza = st.message({ from = module.host; to = occupant.jid; })
|
||||||
|
:tag('json-message', {
|
||||||
|
xmlns = 'http://jitsi.org/jitmeet',
|
||||||
|
room = internal_room_jid_match_rewrite(room.jid)
|
||||||
|
}):text(json_msg):up();
|
||||||
module:send(stanza);
|
module:send(stanza);
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -84,12 +122,14 @@ function room_created(event)
|
|||||||
local room = event.room;
|
local room = event.room;
|
||||||
|
|
||||||
if is_healthcheck_room(room.jid) then
|
if is_healthcheck_room(room.jid) then
|
||||||
return ;
|
return;
|
||||||
end
|
end
|
||||||
|
|
||||||
if not room.jitsiMetadata then
|
if not room.jitsiMetadata then
|
||||||
room.jitsiMetadata = {};
|
room.jitsiMetadata = {};
|
||||||
end
|
end
|
||||||
|
|
||||||
|
room.sent_initial_metadata = {};
|
||||||
end
|
end
|
||||||
|
|
||||||
function on_message(event)
|
function on_message(event)
|
||||||
@ -129,11 +169,6 @@ function on_message(event)
|
|||||||
return false;
|
return false;
|
||||||
end
|
end
|
||||||
|
|
||||||
if occupant.role ~= 'moderator' then
|
|
||||||
module:log('warn', 'Occupant %s is not moderator and not allowed this operation for %s', from, room.jid);
|
|
||||||
return false;
|
|
||||||
end
|
|
||||||
|
|
||||||
local jsonData, error = json.decode(messageText);
|
local jsonData, error = json.decode(messageText);
|
||||||
if jsonData == nil then -- invalid JSON
|
if jsonData == nil then -- invalid JSON
|
||||||
module:log("error", "Invalid JSON message: %s error:%s", messageText, error);
|
module:log("error", "Invalid JSON message: %s error:%s", messageText, error);
|
||||||
@ -145,6 +180,19 @@ function on_message(event)
|
|||||||
return false;
|
return false;
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if occupant.role ~= 'moderator' then
|
||||||
|
-- will return a non nil filtered data to use, if it is nil, it is not allowed
|
||||||
|
local res = module:context(muc_domain_base):fire_event('jitsi-metadata-allow-moderation',
|
||||||
|
{ room = room; actor = occupant; key = jsonData.key ; data = jsonData.data; session = session; });
|
||||||
|
|
||||||
|
if not res then
|
||||||
|
module:log('warn', 'Occupant %s is not moderator and not allowed this operation for %s', from, room.jid);
|
||||||
|
return false;
|
||||||
|
end
|
||||||
|
|
||||||
|
jsonData.data = res;
|
||||||
|
end
|
||||||
|
|
||||||
room.jitsiMetadata[jsonData.key] = jsonData.data;
|
room.jitsiMetadata[jsonData.key] = jsonData.data;
|
||||||
|
|
||||||
broadcastMetadata(room);
|
broadcastMetadata(room);
|
||||||
@ -169,21 +217,39 @@ function process_main_muc_loaded(main_muc, host_module)
|
|||||||
|
|
||||||
host_module:hook("muc-room-created", room_created, -1);
|
host_module:hook("muc-room-created", room_created, -1);
|
||||||
|
|
||||||
host_module:hook('muc-disco#info', function (event)
|
|
||||||
local room = event.room;
|
|
||||||
|
|
||||||
table.insert(event.form, getFormData(room));
|
|
||||||
end);
|
|
||||||
|
|
||||||
host_module:hook("muc-config-form", function(event)
|
|
||||||
local room = event.room;
|
|
||||||
|
|
||||||
table.insert(event.form, getFormData(room));
|
|
||||||
end);
|
|
||||||
-- The room metadata was updated internally (from another module).
|
-- The room metadata was updated internally (from another module).
|
||||||
host_module:hook("room-metadata-changed", function(event)
|
host_module:hook("room-metadata-changed", function(event)
|
||||||
broadcastMetadata(event.room);
|
broadcastMetadata(event.room);
|
||||||
end);
|
end);
|
||||||
|
|
||||||
|
-- TODO: Once clients update to read/write metadata for startMuted policy we can drop this
|
||||||
|
-- this is to convert presence settings from old clients to metadata
|
||||||
|
host_module:hook('muc-broadcast-presence', function (event)
|
||||||
|
local actor, occupant, room, stanza, x = event.actor, event.occupant, event.room, event.stanza, event.x;
|
||||||
|
|
||||||
|
if is_healthcheck_room(room.jid) or occupant.role ~= 'moderator' then
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
|
||||||
|
local startMuted = stanza:get_child('startmuted', 'http://jitsi.org/jitmeet/start-muted');
|
||||||
|
|
||||||
|
if not startMuted then
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
|
||||||
|
if not room.jitsiMetadata then
|
||||||
|
room.jitsiMetadata = {};
|
||||||
|
end
|
||||||
|
|
||||||
|
local startMutedMetadata = room.jitsiMetadata.startMuted or {};
|
||||||
|
|
||||||
|
startMutedMetadata.audio = startMuted.attr.audio == 'true';
|
||||||
|
startMutedMetadata.video = startMuted.attr.video == 'true';
|
||||||
|
|
||||||
|
room.jitsiMetadata.startMuted = startMutedMetadata;
|
||||||
|
|
||||||
|
host_module:fire_event('room-metadata-changed', { room = room; });
|
||||||
|
end);
|
||||||
end
|
end
|
||||||
|
|
||||||
-- process or waits to process the main muc component
|
-- process or waits to process the main muc component
|
||||||
@ -208,18 +274,6 @@ function process_breakout_muc_loaded(breakout_muc, host_module)
|
|||||||
module:log("info", "Hook to muc events on %s", breakout_rooms_component_host);
|
module:log("info", "Hook to muc events on %s", breakout_rooms_component_host);
|
||||||
|
|
||||||
host_module:hook("muc-room-created", room_created, -1);
|
host_module:hook("muc-room-created", room_created, -1);
|
||||||
|
|
||||||
host_module:hook('muc-disco#info', function (event)
|
|
||||||
local room = event.room;
|
|
||||||
|
|
||||||
table.insert(event.form, getFormData(room));
|
|
||||||
end);
|
|
||||||
|
|
||||||
host_module:hook("muc-config-form", function(event)
|
|
||||||
local room = event.room;
|
|
||||||
|
|
||||||
table.insert(event.form, getFormData(room));
|
|
||||||
end);
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if breakout_rooms_component_host then
|
if breakout_rooms_component_host then
|
||||||
@ -238,3 +292,53 @@ if breakout_rooms_component_host then
|
|||||||
end
|
end
|
||||||
end);
|
end);
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- Send a message update for metadata before sending the first self presence
|
||||||
|
function filter_stanza(stanza, session)
|
||||||
|
if not stanza.attr or not stanza.attr.to or stanza.name ~= 'presence'
|
||||||
|
or stanza.attr.type == 'unavailable' or ends_with(stanza.attr.from, '/focus') then
|
||||||
|
return stanza;
|
||||||
|
end
|
||||||
|
|
||||||
|
local bare_to = jid.bare(stanza.attr.to);
|
||||||
|
local muc_x = stanza:get_child('x', MUC_NS..'#user');
|
||||||
|
if not muc_x or not presence_check_status(muc_x, '110') then
|
||||||
|
return stanza;
|
||||||
|
end
|
||||||
|
|
||||||
|
local room = get_room_from_jid(room_jid_match_rewrite(jid.bare(stanza.attr.from)));
|
||||||
|
|
||||||
|
if not room or not room.sent_initial_metadata or is_healthcheck_room(room.jid) then
|
||||||
|
return stanza;
|
||||||
|
end
|
||||||
|
|
||||||
|
if room.sent_initial_metadata[bare_to] then
|
||||||
|
return stanza;
|
||||||
|
end
|
||||||
|
|
||||||
|
local occupant;
|
||||||
|
for _, o in room:each_occupant() do
|
||||||
|
if o.bare_jid == bare_to then
|
||||||
|
occupant = o;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if not occupant then
|
||||||
|
module:log('warn', 'No occupant %s found for %s', bare_to, room.jid);
|
||||||
|
return stanza;
|
||||||
|
end
|
||||||
|
|
||||||
|
room.sent_initial_metadata[bare_to] = true;
|
||||||
|
|
||||||
|
send_metadata(occupant, room);
|
||||||
|
|
||||||
|
return stanza;
|
||||||
|
end
|
||||||
|
function filter_session(session)
|
||||||
|
-- domain mapper is filtering on default priority 0
|
||||||
|
-- allowners is -1 and we need it after that, permissions is -2
|
||||||
|
filters.add_filter(session, 'stanzas/out', filter_stanza, -3);
|
||||||
|
end
|
||||||
|
|
||||||
|
-- enable filtering presences
|
||||||
|
filters.add_filter_hook(filter_session);
|
||||||
|
136
roles/jitsi/files/prosody/modules/mod_short_lived_token.lua
Normal file
136
roles/jitsi/files/prosody/modules/mod_short_lived_token.lua
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
-- to be enabled under the main virtual host with all required settings
|
||||||
|
-- short_lived_token = {
|
||||||
|
-- issuer = 'myissuer';
|
||||||
|
-- accepted_audiences = { 'file-sharing' };
|
||||||
|
-- key_path = '/etc/prosody/short_lived_token.key';
|
||||||
|
-- key_id = 'my_kid';
|
||||||
|
-- ttl_seconds = 30;
|
||||||
|
-- };
|
||||||
|
-- The key in key_path can be generated via: openssl genrsa -out $PRIVATE_KEY_PATH 2048
|
||||||
|
-- And you can get the public key from it, which can be used ot verify those tokens via:
|
||||||
|
-- openssl rsa -in $PRIVATE_KEY_PATH -pubout -out $PUBLIC_KEY_PATH
|
||||||
|
|
||||||
|
local jid = require 'util.jid';
|
||||||
|
local st = require 'util.stanza';
|
||||||
|
local jwt = module:require 'luajwtjitsi';
|
||||||
|
|
||||||
|
local util = module:require 'util';
|
||||||
|
local is_vpaas = util.is_vpaas;
|
||||||
|
local process_host_module = util.process_host_module;
|
||||||
|
local table_find = util.table_find;
|
||||||
|
local create_throttle = require 'prosody.util.throttle'.create;
|
||||||
|
|
||||||
|
local SERVICE_TYPE = 'short-lived-token';
|
||||||
|
local options = module:get_option('short_lived_token');
|
||||||
|
|
||||||
|
if not (options.issuer and options.accepted_audiences
|
||||||
|
and options.key_path and options.key_id and options.ttl_seconds) then
|
||||||
|
module:log('error', 'Missing required options for short_lived_token');
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
|
||||||
|
local f = io.open(options.key_path, 'r');
|
||||||
|
if f then
|
||||||
|
options.key = f:read('*all');
|
||||||
|
f:close();
|
||||||
|
end
|
||||||
|
|
||||||
|
local accepted_requests = {};
|
||||||
|
for _, host in pairs(options.accepted_audiences) do
|
||||||
|
accepted_requests[string.format('%s:%s:0', SERVICE_TYPE, host)] = host;
|
||||||
|
end
|
||||||
|
|
||||||
|
local server_region_name = module:get_option_string('region_name');
|
||||||
|
|
||||||
|
local main_muc_component_host = module:get_option_string('main_muc');
|
||||||
|
if main_muc_component_host == nil then
|
||||||
|
module:log('error', 'main_muc not configured. Cannot proceed.');
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
local main_muc_service;
|
||||||
|
|
||||||
|
function generateToken(session, audience, room, occupant)
|
||||||
|
local t = os.time();
|
||||||
|
local exp = t + options.ttl_seconds;
|
||||||
|
local presence = occupant:get_presence(session.full_jid);
|
||||||
|
local _, _, id = extract_subdomain(jid.node(room.jid));
|
||||||
|
|
||||||
|
local payload = {
|
||||||
|
iss = options.issuer,
|
||||||
|
aud = audience,
|
||||||
|
nbf = t,
|
||||||
|
exp = exp,
|
||||||
|
sub = session.jitsi_web_query_prefix or module.host,
|
||||||
|
context = {
|
||||||
|
group = session.jitsi_meet_context_group or session.granted_jitsi_meet_context_group,
|
||||||
|
user = session.jitsi_meet_context_user or {
|
||||||
|
id = session.full_jid,
|
||||||
|
name = presence:get_child_text('nick', 'http://jabber.org/protocol/nick'),
|
||||||
|
email = presence:get_child_text("email") or nil,
|
||||||
|
nick = jid.resource(occupant.nick)
|
||||||
|
},
|
||||||
|
features = session.jitsi_meet_context_features or session.granted_jitsi_meet_context_features
|
||||||
|
},
|
||||||
|
room = session.jitsi_web_query_room,
|
||||||
|
meeting_id = room._data.meetingId,
|
||||||
|
granted_from = session.granted_jitsi_meet_context_user_id,
|
||||||
|
customer_id = id or session.jitsi_meet_context_group or session.granted_jitsi_meet_context_group,
|
||||||
|
backend_region = server_region_name,
|
||||||
|
user_region = session.user_region
|
||||||
|
};
|
||||||
|
|
||||||
|
local alg = 'RS256';
|
||||||
|
local token, err = jwt.encode(payload, options.key, alg, { kid = options.key_id });
|
||||||
|
if not err then
|
||||||
|
return token
|
||||||
|
else
|
||||||
|
module:log('error', 'Error generating token: %s', err);
|
||||||
|
return ''
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
module:hook('external_service/credentials', function (event)
|
||||||
|
local requested_credentials, services, session, stanza
|
||||||
|
= event.requested_credentials, event.services, event.origin, event.stanza;
|
||||||
|
local room = get_room_by_name_and_subdomain(session.jitsi_web_query_room, session.jitsi_web_query_prefix);
|
||||||
|
|
||||||
|
if not room then
|
||||||
|
session.send(st.error_reply(stanza, 'cancel', 'not-allowed'));
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
|
||||||
|
local occupant = room:get_occupant_by_real_jid(session.full_jid);
|
||||||
|
if not occupant then
|
||||||
|
session.send(st.error_reply(stanza, 'cancel', 'not-allowed'));
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
|
||||||
|
for request in requested_credentials do
|
||||||
|
local host = accepted_requests[request];
|
||||||
|
if host then
|
||||||
|
services:push({
|
||||||
|
type = SERVICE_TYPE;
|
||||||
|
host = host;
|
||||||
|
username = 'token';
|
||||||
|
password = generateToken(session, host, room, occupant);
|
||||||
|
expires = os.time() + options.ttl_seconds;
|
||||||
|
restricted = true;
|
||||||
|
});
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end);
|
||||||
|
|
||||||
|
process_host_module(main_muc_component_host, function(host_module, host)
|
||||||
|
local muc_module = prosody.hosts[host].modules.muc;
|
||||||
|
|
||||||
|
if muc_module then
|
||||||
|
main_muc_service = muc_module;
|
||||||
|
else
|
||||||
|
prosody.hosts[host].events.add_handler('module-loaded', function(event)
|
||||||
|
if (event.module == 'muc') then
|
||||||
|
main_muc_service = prosody.hosts[host].modules.muc;
|
||||||
|
end
|
||||||
|
end);
|
||||||
|
end
|
||||||
|
end);
|
@ -1,13 +1,15 @@
|
|||||||
local util = module:require "util";
|
local util = module:require "util";
|
||||||
|
local is_admin = util.is_admin;
|
||||||
local get_room_from_jid = util.get_room_from_jid;
|
local get_room_from_jid = util.get_room_from_jid;
|
||||||
local room_jid_match_rewrite = util.room_jid_match_rewrite;
|
local room_jid_match_rewrite = util.room_jid_match_rewrite;
|
||||||
|
local is_jibri = util.is_jibri;
|
||||||
local is_healthcheck_room = util.is_healthcheck_room;
|
local is_healthcheck_room = util.is_healthcheck_room;
|
||||||
local process_host_module = util.process_host_module;
|
local process_host_module = util.process_host_module;
|
||||||
|
local is_transcriber_jigasi = util.is_transcriber_jigasi;
|
||||||
local jid_resource = require "util.jid".resource;
|
local jid_resource = require "util.jid".resource;
|
||||||
local st = require "util.stanza";
|
local st = require "util.stanza";
|
||||||
local socket = require "socket";
|
local socket = require "socket";
|
||||||
local json = require 'cjson.safe';
|
local json = require 'cjson.safe';
|
||||||
local um_is_admin = require "core.usermanager".is_admin;
|
|
||||||
local jid_split = require 'util.jid'.split;
|
local jid_split = require 'util.jid'.split;
|
||||||
|
|
||||||
-- we use async to detect Prosody 0.10 and earlier
|
-- we use async to detect Prosody 0.10 and earlier
|
||||||
@ -30,10 +32,6 @@ module:log("info", "Starting speakerstats for %s", muc_component_host);
|
|||||||
|
|
||||||
local main_muc_service;
|
local main_muc_service;
|
||||||
|
|
||||||
local function is_admin(jid)
|
|
||||||
return um_is_admin(jid, module.host);
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Searches all rooms in the main muc component that holds a breakout room
|
-- Searches all rooms in the main muc component that holds a breakout room
|
||||||
-- caches it if found so we don't search it again
|
-- caches it if found so we don't search it again
|
||||||
-- we should not cache objects in _data as this is being serialized when calling room:save()
|
-- we should not cache objects in _data as this is being serialized when calling room:save()
|
||||||
@ -221,14 +219,15 @@ end
|
|||||||
|
|
||||||
-- Create SpeakerStats object for the joined user
|
-- Create SpeakerStats object for the joined user
|
||||||
function occupant_joined(event)
|
function occupant_joined(event)
|
||||||
local occupant, room = event.occupant, event.room;
|
local occupant, room, stanza = event.occupant, event.room, event.stanza;
|
||||||
|
|
||||||
if is_healthcheck_room(room.jid) or is_admin(occupant.bare_jid) then
|
if is_healthcheck_room(room.jid)
|
||||||
|
or is_admin(occupant.bare_jid)
|
||||||
|
or is_transcriber_jigasi(stanza)
|
||||||
|
or is_jibri(occupant) then
|
||||||
return;
|
return;
|
||||||
end
|
end
|
||||||
|
|
||||||
local occupant = event.occupant;
|
|
||||||
|
|
||||||
local nick = jid_resource(occupant.nick);
|
local nick = jid_resource(occupant.nick);
|
||||||
|
|
||||||
if room.speakerStats then
|
if room.speakerStats then
|
||||||
|
@ -15,7 +15,6 @@ local get_room_from_jid = util.get_room_from_jid;
|
|||||||
local st = require "util.stanza";
|
local st = require "util.stanza";
|
||||||
local json = require "cjson.safe";
|
local json = require "cjson.safe";
|
||||||
|
|
||||||
local muc_domain_base = module:get_option_string("muc_mapper_domain_base");
|
|
||||||
local asapKeyServer = module:get_option_string("prosody_password_public_key_repo_url", "");
|
local asapKeyServer = module:get_option_string("prosody_password_public_key_repo_url", "");
|
||||||
|
|
||||||
if asapKeyServer then
|
if asapKeyServer then
|
||||||
|
@ -4,19 +4,17 @@
|
|||||||
local log = module._log;
|
local log = module._log;
|
||||||
local host = module.host;
|
local host = module.host;
|
||||||
local st = require "util.stanza";
|
local st = require "util.stanza";
|
||||||
local um_is_admin = require "core.usermanager".is_admin;
|
|
||||||
local jid_split = require 'util.jid'.split;
|
local jid_split = require 'util.jid'.split;
|
||||||
local jid_bare = require 'util.jid'.bare;
|
local jid_bare = require 'util.jid'.bare;
|
||||||
|
|
||||||
|
local util = module:require 'util';
|
||||||
|
local is_admin = util.is_admin;
|
||||||
|
|
||||||
local DEBUG = false;
|
local DEBUG = false;
|
||||||
|
|
||||||
local measure_success = module:measure('success', 'counter');
|
local measure_success = module:measure('success', 'counter');
|
||||||
local measure_fail = module:measure('fail', 'counter');
|
local measure_fail = module:measure('fail', 'counter');
|
||||||
|
|
||||||
local function is_admin(jid)
|
|
||||||
return um_is_admin(jid, host);
|
|
||||||
end
|
|
||||||
|
|
||||||
local parentHostName = string.gmatch(tostring(host), "%w+.(%w.+)")();
|
local parentHostName = string.gmatch(tostring(host), "%w+.(%w.+)")();
|
||||||
if parentHostName == nil then
|
if parentHostName == nil then
|
||||||
module:log("error", "Failed to start - unable to get parent hostname");
|
module:log("error", "Failed to start - unable to get parent hostname");
|
||||||
|
@ -12,13 +12,10 @@ local st = require 'util.stanza';
|
|||||||
local jid = require 'util.jid';
|
local jid = require 'util.jid';
|
||||||
local new_id = require 'util.id'.medium;
|
local new_id = require 'util.id'.medium;
|
||||||
local util = module:require 'util';
|
local util = module:require 'util';
|
||||||
|
local is_admin = util.is_admin;
|
||||||
local presence_check_status = util.presence_check_status;
|
local presence_check_status = util.presence_check_status;
|
||||||
local process_host_module = util.process_host_module;
|
local process_host_module = util.process_host_module;
|
||||||
|
local is_transcriber_jigasi = util.is_transcriber_jigasi;
|
||||||
local um_is_admin = require 'core.usermanager'.is_admin;
|
|
||||||
local function is_admin(jid)
|
|
||||||
return um_is_admin(jid, module.host);
|
|
||||||
end
|
|
||||||
|
|
||||||
local MUC_NS = 'http://jabber.org/protocol/muc';
|
local MUC_NS = 'http://jabber.org/protocol/muc';
|
||||||
|
|
||||||
@ -57,7 +54,7 @@ local function send_visitors_iq(conference_service, room, type)
|
|||||||
-- send iq informing the vnode that the connect is done and it will allow visitors to join
|
-- send iq informing the vnode that the connect is done and it will allow visitors to join
|
||||||
local iq_id = new_id();
|
local iq_id = new_id();
|
||||||
sent_iq_cache:set(iq_id, socket.gettime());
|
sent_iq_cache:set(iq_id, socket.gettime());
|
||||||
local connect_done = st.iq({
|
local visitors_iq = st.iq({
|
||||||
type = 'set',
|
type = 'set',
|
||||||
to = conference_service,
|
to = conference_service,
|
||||||
from = module.host,
|
from = module.host,
|
||||||
@ -68,11 +65,24 @@ local function send_visitors_iq(conference_service, room, type)
|
|||||||
password = type ~= 'disconnect' and room:get_password() or '',
|
password = type ~= 'disconnect' and room:get_password() or '',
|
||||||
lobby = room._data.lobbyroom and 'true' or 'false',
|
lobby = room._data.lobbyroom and 'true' or 'false',
|
||||||
meetingId = room._data.meetingId,
|
meetingId = room._data.meetingId,
|
||||||
moderatorId = room._data.moderator_id, -- can be used from external modules to set single moderator for meetings
|
|
||||||
createdTimestamp = room.created_timestamp and tostring(room.created_timestamp) or nil
|
createdTimestamp = room.created_timestamp and tostring(room.created_timestamp) or nil
|
||||||
}):up();
|
});
|
||||||
|
|
||||||
module:send(connect_done);
|
if type == 'update' then
|
||||||
|
visitors_iq:tag('moderators', { xmlns = 'jitsi:visitors' });
|
||||||
|
|
||||||
|
for _, o in room:each_occupant() do
|
||||||
|
if not is_admin(o.bare_jid) and o.role == 'moderator' then
|
||||||
|
visitors_iq:tag('item', { epId = jid.resource(o.nick) }):up();
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
visitors_iq:up();
|
||||||
|
end
|
||||||
|
|
||||||
|
visitors_iq:up();
|
||||||
|
|
||||||
|
module:send(visitors_iq);
|
||||||
end
|
end
|
||||||
|
|
||||||
-- an event received from visitors component, which receives iqs from jicofo
|
-- an event received from visitors component, which receives iqs from jicofo
|
||||||
@ -96,6 +106,9 @@ local function connect_vnode(event)
|
|||||||
|
|
||||||
local sent_main_participants = 0;
|
local sent_main_participants = 0;
|
||||||
|
|
||||||
|
-- send update initially so we can report the moderators that will join
|
||||||
|
send_visitors_iq(conference_service, room, 'update');
|
||||||
|
|
||||||
for _, o in room:each_occupant() do
|
for _, o in room:each_occupant() do
|
||||||
if not is_admin(o.bare_jid) then
|
if not is_admin(o.bare_jid) then
|
||||||
local fmuc_pr = st.clone(o:get_presence());
|
local fmuc_pr = st.clone(o:get_presence());
|
||||||
@ -185,7 +198,7 @@ process_host_module(main_muc_component_config, function(host_module, host)
|
|||||||
|
|
||||||
-- filter focus and configured domains (used for jibri and transcribers)
|
-- filter focus and configured domains (used for jibri and transcribers)
|
||||||
if is_admin(stanza.attr.from) or visitors_nodes[room.jid] == nil
|
if is_admin(stanza.attr.from) or visitors_nodes[room.jid] == nil
|
||||||
or ignore_list:contains(jid.host(occupant.bare_jid)) then
|
or (ignore_list:contains(jid.host(occupant.bare_jid)) and not is_transcriber_jigasi(stanza)) then
|
||||||
return;
|
return;
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -206,7 +219,7 @@ process_host_module(main_muc_component_config, function(host_module, host)
|
|||||||
|
|
||||||
-- ignore configured domains (jibri and transcribers)
|
-- ignore configured domains (jibri and transcribers)
|
||||||
if is_admin(occupant.bare_jid) or visitors_nodes[room.jid] == nil or visitors_nodes[room.jid].nodes == nil
|
if is_admin(occupant.bare_jid) or visitors_nodes[room.jid] == nil or visitors_nodes[room.jid].nodes == nil
|
||||||
or ignore_list:contains(jid.host(occupant.bare_jid)) then
|
or (ignore_list:contains(jid.host(occupant.bare_jid)) and not is_transcriber_jigasi(stanza)) then
|
||||||
return;
|
return;
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -249,7 +262,7 @@ process_host_module(main_muc_component_config, function(host_module, host)
|
|||||||
|
|
||||||
-- filter focus, ignore configured domains (jibri and transcribers)
|
-- filter focus, ignore configured domains (jibri and transcribers)
|
||||||
if is_admin(stanza.attr.from) or visitors_nodes[room.jid] == nil
|
if is_admin(stanza.attr.from) or visitors_nodes[room.jid] == nil
|
||||||
or ignore_list:contains(jid.host(occupant.bare_jid)) then
|
or (ignore_list:contains(jid.host(occupant.bare_jid)) and not is_transcriber_jigasi(stanza)) then
|
||||||
return;
|
return;
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -257,6 +270,10 @@ process_host_module(main_muc_component_config, function(host_module, host)
|
|||||||
local user, _, res = jid.split(occupant.nick);
|
local user, _, res = jid.split(occupant.nick);
|
||||||
-- a main participant we need to update all active visitor nodes
|
-- a main participant we need to update all active visitor nodes
|
||||||
for k in pairs(vnodes) do
|
for k in pairs(vnodes) do
|
||||||
|
if occupant.role == 'moderator' then
|
||||||
|
-- first send that the participant is a moderator
|
||||||
|
send_visitors_iq(k, room, 'update');
|
||||||
|
end
|
||||||
local fmuc_pr = st.clone(stanza);
|
local fmuc_pr = st.clone(stanza);
|
||||||
fmuc_pr.attr.to = jid.join(user, k, res);
|
fmuc_pr.attr.to = jid.join(user, k, res);
|
||||||
fmuc_pr.attr.from = occupant.jid;
|
fmuc_pr.attr.from = occupant.jid;
|
||||||
@ -268,7 +285,7 @@ process_host_module(main_muc_component_config, function(host_module, host)
|
|||||||
local room, stanza, occupant = event.room, event.stanza, event.occupant;
|
local room, stanza, occupant = event.room, event.stanza, event.occupant;
|
||||||
|
|
||||||
-- filter sending messages from transcribers/jibris to visitors
|
-- filter sending messages from transcribers/jibris to visitors
|
||||||
if not visitors_nodes[room.jid] or ignore_list:contains(jid.host(occupant.bare_jid)) then
|
if not visitors_nodes[room.jid] then
|
||||||
return;
|
return;
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -296,6 +313,11 @@ process_host_module(main_muc_component_config, function(host_module, host)
|
|||||||
return;
|
return;
|
||||||
end
|
end
|
||||||
|
|
||||||
|
if host_module:fire_event('jitsi-visitor-groupchat-pre-route', event) then
|
||||||
|
-- message filtered
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
|
||||||
-- a message from visitor occupant of known visitor node
|
-- a message from visitor occupant of known visitor node
|
||||||
stanza.attr.from = to;
|
stanza.attr.from = to;
|
||||||
for _, o in room:each_occupant() do
|
for _, o in room:each_occupant() do
|
||||||
@ -331,6 +353,21 @@ process_host_module(main_muc_component_config, function(host_module, host)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end, -100); -- we want to run last in order to check is the status code 104
|
end, -100); -- we want to run last in order to check is the status code 104
|
||||||
|
|
||||||
|
host_module:hook('muc-set-affiliation', function (event)
|
||||||
|
if event.actor and not is_admin(event.actor) and event.affiliation == 'owner' then
|
||||||
|
local room = event.room;
|
||||||
|
|
||||||
|
if not visitors_nodes[room.jid] then
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
-- we need to update all vnodes
|
||||||
|
local vnodes = visitors_nodes[room.jid].nodes;
|
||||||
|
for conference_service in pairs(vnodes) do
|
||||||
|
send_visitors_iq(conference_service, room, 'update');
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end, -2);
|
||||||
end);
|
end);
|
||||||
|
|
||||||
module:hook('jitsi-lobby-enabled', function(event)
|
module:hook('jitsi-lobby-enabled', function(event)
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
module:log('info', 'Starting visitors_component at %s', module.host);
|
module:log('info', 'Starting visitors_component at %s', module.host);
|
||||||
|
|
||||||
|
local array = require "util.array";
|
||||||
local http = require 'net.http';
|
local http = require 'net.http';
|
||||||
local jid = require 'util.jid';
|
local jid = require 'util.jid';
|
||||||
local st = require 'util.stanza';
|
local st = require 'util.stanza';
|
||||||
local util = module:require 'util';
|
local util = module:require 'util';
|
||||||
|
local is_admin = util.is_admin;
|
||||||
local is_healthcheck_room = util.is_healthcheck_room;
|
local is_healthcheck_room = util.is_healthcheck_room;
|
||||||
local is_sip_jigasi = util.is_sip_jigasi;
|
local is_sip_jigasi = util.is_sip_jigasi;
|
||||||
local room_jid_match_rewrite = util.room_jid_match_rewrite;
|
local room_jid_match_rewrite = util.room_jid_match_rewrite;
|
||||||
@ -11,11 +13,13 @@ local get_room_from_jid = util.get_room_from_jid;
|
|||||||
local get_focus_occupant = util.get_focus_occupant;
|
local get_focus_occupant = util.get_focus_occupant;
|
||||||
local get_room_by_name_and_subdomain = util.get_room_by_name_and_subdomain;
|
local get_room_by_name_and_subdomain = util.get_room_by_name_and_subdomain;
|
||||||
local internal_room_jid_match_rewrite = util.internal_room_jid_match_rewrite;
|
local internal_room_jid_match_rewrite = util.internal_room_jid_match_rewrite;
|
||||||
|
local table_find = util.table_find;
|
||||||
local is_vpaas = util.is_vpaas;
|
local is_vpaas = util.is_vpaas;
|
||||||
local is_sip_jibri_join = util.is_sip_jibri_join;
|
local is_sip_jibri_join = util.is_sip_jibri_join;
|
||||||
local process_host_module = util.process_host_module;
|
local process_host_module = util.process_host_module;
|
||||||
|
local respond_iq_result = util.respond_iq_result;
|
||||||
|
local split_string = util.split_string;
|
||||||
local new_id = require 'util.id'.medium;
|
local new_id = require 'util.id'.medium;
|
||||||
local um_is_admin = require 'core.usermanager'.is_admin;
|
|
||||||
local json = require 'cjson.safe';
|
local json = require 'cjson.safe';
|
||||||
local inspect = require 'inspect';
|
local inspect = require 'inspect';
|
||||||
|
|
||||||
@ -47,10 +51,6 @@ local http_headers = {
|
|||||||
["Accept"] = "application/json"
|
["Accept"] = "application/json"
|
||||||
};
|
};
|
||||||
|
|
||||||
local function is_admin(jid)
|
|
||||||
return um_is_admin(jid, module.host);
|
|
||||||
end
|
|
||||||
|
|
||||||
-- This is a map to keep data for room and the jids that were allowed to join after visitor mode is enabled
|
-- This is a map to keep data for room and the jids that were allowed to join after visitor mode is enabled
|
||||||
-- automatically allowed or allowed by a moderator
|
-- automatically allowed or allowed by a moderator
|
||||||
local visitors_promotion_map = {};
|
local visitors_promotion_map = {};
|
||||||
@ -63,17 +63,6 @@ local visitors_promotion_requests = {};
|
|||||||
local cache = require 'util.cache';
|
local cache = require 'util.cache';
|
||||||
local sent_iq_cache = cache.new(200);
|
local sent_iq_cache = cache.new(200);
|
||||||
|
|
||||||
-- send iq result that the iq was received and will be processed
|
|
||||||
local function respond_iq_result(origin, stanza)
|
|
||||||
-- respond with successful receiving the iq
|
|
||||||
origin.send(st.iq({
|
|
||||||
type = 'result';
|
|
||||||
from = stanza.attr.to;
|
|
||||||
to = stanza.attr.from;
|
|
||||||
id = stanza.attr.id
|
|
||||||
}));
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Sends a json-message to the destination jid
|
-- Sends a json-message to the destination jid
|
||||||
-- @param to_jid the destination jid
|
-- @param to_jid the destination jid
|
||||||
-- @param json_message the message content to send
|
-- @param json_message the message content to send
|
||||||
@ -83,13 +72,33 @@ function send_json_message(to_jid, json_message)
|
|||||||
module:send(stanza);
|
module:send(stanza);
|
||||||
end
|
end
|
||||||
|
|
||||||
local function request_promotion_received(room, from_jid, from_vnode, nick, time, user_id, force_promote)
|
local function request_promotion_received(room, from_jid, from_vnode, nick, time, user_id, group_id, force_promote_requested)
|
||||||
-- if visitors is enabled for the room
|
-- if visitors is enabled for the room
|
||||||
if visitors_promotion_map[room.jid] then
|
if visitors_promotion_map[room.jid] then
|
||||||
|
local force_promote = auto_allow_promotion;
|
||||||
|
if not force_promote and force_promote_requested == 'true' then
|
||||||
|
-- Let's do the force_promote checks if requested
|
||||||
|
-- if it is vpaas meeting we trust the moderator computation from visitor node (value of force_promote_requested)
|
||||||
|
-- if it is not vpaas we need to check further settings only if they exist
|
||||||
|
if is_vpaas(room) or (not room._data.moderator_id and not room._data.moderators)
|
||||||
|
-- _data.moderator_id can be used from external modules to set single moderator for a meeting
|
||||||
|
-- or a whole group of moderators
|
||||||
|
or (room._data.moderator_id
|
||||||
|
and room._data.moderator_id == user_id or room._data.moderator_id == group_id)
|
||||||
|
|
||||||
|
-- all moderators are allowed to auto promote, the fact that user_id and force_promote_requested are set
|
||||||
|
-- means that the user has token and is moderator on visitor node side
|
||||||
|
or room._data.allModerators
|
||||||
|
|
||||||
|
-- can be used by external modules to set multiple moderator ids (table of values)
|
||||||
|
or table_find(room._data.moderators, user_id)
|
||||||
|
then
|
||||||
|
force_promote = true;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
-- only for raise hand, ignore lowering the hand
|
-- only for raise hand, ignore lowering the hand
|
||||||
if time and time > 0 and (
|
if time and time > 0 and force_promote then
|
||||||
auto_allow_promotion
|
|
||||||
or force_promote == 'true') then
|
|
||||||
-- we are in auto-allow mode, let's reply with accept
|
-- we are in auto-allow mode, let's reply with accept
|
||||||
-- we store where the request is coming from so we can send back the response
|
-- we store where the request is coming from so we can send back the response
|
||||||
local username = new_id():lower();
|
local username = new_id():lower();
|
||||||
@ -179,7 +188,7 @@ local function connect_vnode_received(room, vnode)
|
|||||||
room._connected_vnodes = cache.new(16); -- we up to 16 vnodes for this prosody
|
room._connected_vnodes = cache.new(16); -- we up to 16 vnodes for this prosody
|
||||||
end
|
end
|
||||||
|
|
||||||
room._connected_vnodes:set(vnode..'.meet.jitsi', 'connected');
|
room._connected_vnodes:set(vnode..'.meet.jitsi', {});
|
||||||
end
|
end
|
||||||
|
|
||||||
local function disconnect_vnode_received(room, vnode)
|
local function disconnect_vnode_received(room, vnode)
|
||||||
@ -194,6 +203,44 @@ local function disconnect_vnode_received(room, vnode)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- returns the accumulated data for visitors nodes, count all visitors requesting transcriptions
|
||||||
|
-- and accumulated languages requested
|
||||||
|
-- @returns count, languages
|
||||||
|
function get_visitors_languages(room)
|
||||||
|
if not room._connected_vnodes then
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
|
||||||
|
local count = 0;
|
||||||
|
local languages = array();
|
||||||
|
|
||||||
|
-- iterate over visitor nodes we are connected to and accumulate data if we have it
|
||||||
|
for k, v in room._connected_vnodes:items() do
|
||||||
|
if v.count then
|
||||||
|
count = count + v.count;
|
||||||
|
end
|
||||||
|
if v.langs then
|
||||||
|
for k in pairs(v.langs) do
|
||||||
|
local val = v.langs[k]
|
||||||
|
if not languages[val] then
|
||||||
|
languages:push(val);
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return count, languages:sort():concat(',');
|
||||||
|
end
|
||||||
|
|
||||||
|
local function get_visitors_room_metadata(room)
|
||||||
|
if not room.jitsiMetadata then
|
||||||
|
room.jitsiMetadata = {};
|
||||||
|
end
|
||||||
|
if not room.jitsiMetadata.visitors then
|
||||||
|
room.jitsiMetadata.visitors = {};
|
||||||
|
end
|
||||||
|
return room.jitsiMetadata.visitors;
|
||||||
|
end
|
||||||
|
|
||||||
-- listens for iq request for promotion and forward it to moderators in the meeting for approval
|
-- listens for iq request for promotion and forward it to moderators in the meeting for approval
|
||||||
-- or auto-allow it if such the config is set enabling it
|
-- or auto-allow it if such the config is set enabling it
|
||||||
local function stanza_handler(event)
|
local function stanza_handler(event)
|
||||||
@ -229,15 +276,20 @@ local function stanza_handler(event)
|
|||||||
if not room then
|
if not room then
|
||||||
-- this maybe as we receive the iq from jicofo after the room is already destroyed
|
-- this maybe as we receive the iq from jicofo after the room is already destroyed
|
||||||
module:log('debug', 'No room found %s', room_jid);
|
module:log('debug', 'No room found %s', room_jid);
|
||||||
return;
|
return true;
|
||||||
|
end
|
||||||
|
|
||||||
|
local from_vnode;
|
||||||
|
if room._connected_vnodes then
|
||||||
|
from_vnode = room._connected_vnodes:get(stanza.attr.from);
|
||||||
end
|
end
|
||||||
|
|
||||||
local processed;
|
local processed;
|
||||||
-- promotion request is coming from visitors and is a set and is over the s2s connection
|
-- promotion request is coming from visitors and is a set and is over the s2s connection
|
||||||
local request_promotion = visitors_iq:get_child('promotion-request');
|
local request_promotion = visitors_iq:get_child('promotion-request');
|
||||||
if request_promotion then
|
if request_promotion then
|
||||||
if not (room._connected_vnodes and room._connected_vnodes:get(stanza.attr.from)) then
|
if not from_vnode then
|
||||||
module:log('warn', 'Received forged promotion-request: %s %s %s', stanza, inspect(room._connected_vnodes), room._connected_vnodes:get(stanza.attr.from));
|
module:log('warn', 'Received forged request_promotion message: %s %s',stanza, inspect(room._connected_vnodes));
|
||||||
return true; -- stop processing
|
return true; -- stop processing
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -249,6 +301,7 @@ local function stanza_handler(event)
|
|||||||
display_name,
|
display_name,
|
||||||
tonumber(request_promotion.attr.time),
|
tonumber(request_promotion.attr.time),
|
||||||
request_promotion.attr.userId,
|
request_promotion.attr.userId,
|
||||||
|
request_promotion.attr.groupId,
|
||||||
request_promotion.attr.forcePromote
|
request_promotion.attr.forcePromote
|
||||||
);
|
);
|
||||||
end
|
end
|
||||||
@ -266,6 +319,40 @@ local function stanza_handler(event)
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- request to update metadata service for jigasi languages
|
||||||
|
local transcription_languages = visitors_iq:get_child('transcription-languages');
|
||||||
|
|
||||||
|
if transcription_languages
|
||||||
|
and (transcription_languages.attr.langs or transcription_languages.attr.count) then
|
||||||
|
if not from_vnode then
|
||||||
|
module:log('warn', 'Received forged transcription_languages message: %s %s',stanza, inspect(room._connected_vnodes));
|
||||||
|
return true; -- stop processing
|
||||||
|
end
|
||||||
|
|
||||||
|
local metadata = get_visitors_room_metadata(room);
|
||||||
|
|
||||||
|
-- we keep the split by languages array to optimize accumulating languages
|
||||||
|
from_vnode.langs = split_string(transcription_languages.attr.langs, ',');
|
||||||
|
from_vnode.count = transcription_languages.attr.count;
|
||||||
|
|
||||||
|
local count, languages = get_visitors_languages(room);
|
||||||
|
|
||||||
|
if metadata.transcribingLanguages ~= languages then
|
||||||
|
metadata.transcribingLanguages = languages;
|
||||||
|
processed = true;
|
||||||
|
end
|
||||||
|
|
||||||
|
if metadata.transcribingCount ~= count then
|
||||||
|
metadata.transcribingCount = count;
|
||||||
|
processed = true;
|
||||||
|
end
|
||||||
|
|
||||||
|
if processed then
|
||||||
|
module:context(muc_domain_prefix..'.'..muc_domain_base)
|
||||||
|
:fire_event('room-metadata-changed', { room = room; });
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
if not processed then
|
if not processed then
|
||||||
module:log('warn', 'Unknown iq received for %s: %s', module.host, stanza);
|
module:log('warn', 'Unknown iq received for %s: %s', module.host, stanza);
|
||||||
end
|
end
|
||||||
@ -275,6 +362,11 @@ local function stanza_handler(event)
|
|||||||
end
|
end
|
||||||
|
|
||||||
local function process_promotion_response(room, id, approved)
|
local function process_promotion_response(room, id, approved)
|
||||||
|
if not approved then
|
||||||
|
module:log('debug', 'promotion not approved %s, %s', room.jid, id);
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
|
||||||
-- lets reply to participant that requested promotion
|
-- lets reply to participant that requested promotion
|
||||||
local username = new_id():lower();
|
local username = new_id():lower();
|
||||||
visitors_promotion_map[room.jid][username] = {
|
visitors_promotion_map[room.jid][username] = {
|
||||||
@ -312,7 +404,9 @@ local function go_live(room)
|
|||||||
return;
|
return;
|
||||||
end
|
end
|
||||||
|
|
||||||
if not (room.jitsiMetadata and room.jitsiMetadata.visitors and room.jitsiMetadata.visitors.live) then
|
-- if missing we assume room is live, only skip if it is marked explicitly as false
|
||||||
|
if room.jitsiMetadata and room.jitsiMetadata.visitors
|
||||||
|
and room.jitsiMetadata.visitors.live ~= nil and room.jitsiMetadata.visitors.live == false then
|
||||||
return;
|
return;
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -387,10 +481,23 @@ process_host_module(muc_domain_prefix..'.'..muc_domain_base, function(host_modul
|
|||||||
end
|
end
|
||||||
|
|
||||||
if visitors_promotion_map[room.jid] then
|
if visitors_promotion_map[room.jid] then
|
||||||
|
local in_ignore_list = ignore_list:contains(jid.host(stanza.attr.from));
|
||||||
|
|
||||||
-- now let's check for jid
|
-- now let's check for jid
|
||||||
if visitors_promotion_map[room.jid][jid.node(stanza.attr.from)] -- promotion was approved
|
if visitors_promotion_map[room.jid][jid.node(stanza.attr.from)] -- promotion was approved
|
||||||
or ignore_list:contains(jid.host(stanza.attr.from)) then -- jibri or other domains to ignore
|
or in_ignore_list then -- jibri or other domains to ignore
|
||||||
-- allow join
|
-- allow join
|
||||||
|
if not in_ignore_list then
|
||||||
|
-- let's update metadata
|
||||||
|
local metadata = get_visitors_room_metadata(room);
|
||||||
|
if not metadata.promoted then
|
||||||
|
metadata.promoted = {};
|
||||||
|
end
|
||||||
|
metadata.promoted[jid.resource(occupant.nick)] = true;
|
||||||
|
module:context(muc_domain_prefix..'.'..muc_domain_base)
|
||||||
|
:fire_event('room-metadata-changed', { room = room; });
|
||||||
|
end
|
||||||
|
|
||||||
return;
|
return;
|
||||||
end
|
end
|
||||||
module:log('error', 'Visitor needs to be allowed by a moderator %s', stanza.attr.from);
|
module:log('error', 'Visitor needs to be allowed by a moderator %s', stanza.attr.from);
|
||||||
@ -446,17 +553,11 @@ process_host_module(muc_domain_prefix..'.'..muc_domain_base, function(host_modul
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end);
|
end);
|
||||||
host_module:hook("message/bare", function(event)
|
|
||||||
local stanza = event.stanza;
|
|
||||||
|
|
||||||
if stanza.attr.type ~= "groupchat" then
|
host_module:hook('jitsi-endpoint-message-received', function(event)
|
||||||
return;
|
local data, error, occupant, room, stanza
|
||||||
end
|
= event.message, event.error, event.occupant, event.room, event.stanza;
|
||||||
local json_data = stanza:get_child_text("json-message", "http://jitsi.org/jitmeet");
|
|
||||||
if json_data == nil then
|
|
||||||
return;
|
|
||||||
end
|
|
||||||
local data, error = json.decode(json_data);
|
|
||||||
if not data or data.type ~= 'visitors'
|
if not data or data.type ~= 'visitors'
|
||||||
or (data.action ~= "promotion-response" and data.action ~= "demote-request") then
|
or (data.action ~= "promotion-response" and data.action ~= "demote-request") then
|
||||||
if error then
|
if error then
|
||||||
@ -465,17 +566,9 @@ process_host_module(muc_domain_prefix..'.'..muc_domain_base, function(host_modul
|
|||||||
return;
|
return;
|
||||||
end
|
end
|
||||||
|
|
||||||
local room = get_room_from_jid(event.stanza.attr.to);
|
|
||||||
|
|
||||||
local occupant_jid = event.stanza.attr.from;
|
|
||||||
local occupant = room:get_occupant_by_real_jid(occupant_jid);
|
|
||||||
if not occupant then
|
|
||||||
module:log("error", "Occupant %s was not found in room %s", occupant_jid, room.jid)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
if occupant.role ~= 'moderator' then
|
if occupant.role ~= 'moderator' then
|
||||||
module:log('error', 'Occupant %s sending response message but not moderator in room %s',
|
module:log('error', 'Occupant %s sending response message but not moderator in room %s',
|
||||||
occupant_jid, room.jid);
|
occupant.jid, room.jid);
|
||||||
return false;
|
return false;
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -501,7 +594,6 @@ process_host_module(muc_domain_prefix..'.'..muc_domain_base, function(host_modul
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
else
|
else
|
||||||
if data.id then
|
if data.id then
|
||||||
process_promotion_response(room, data.id, data.approved and 'true' or 'false');
|
process_promotion_response(room, data.id, data.approved and 'true' or 'false');
|
||||||
@ -515,6 +607,7 @@ process_host_module(muc_domain_prefix..'.'..muc_domain_base, function(host_modul
|
|||||||
|
|
||||||
return true; -- halt processing, but return true that we handled it
|
return true; -- halt processing, but return true that we handled it
|
||||||
end);
|
end);
|
||||||
|
|
||||||
if visitors_queue_service then
|
if visitors_queue_service then
|
||||||
host_module:hook('muc-room-created', function (event)
|
host_module:hook('muc-room-created', function (event)
|
||||||
local room = event.room;
|
local room = event.room;
|
||||||
@ -535,7 +628,13 @@ process_host_module(muc_domain_prefix..'.'..muc_domain_base, function(host_modul
|
|||||||
go_live(event.room);
|
go_live(event.room);
|
||||||
end);
|
end);
|
||||||
host_module:hook('muc-occupant-joined', function (event)
|
host_module:hook('muc-occupant-joined', function (event)
|
||||||
go_live(event.room);
|
local room = event.room;
|
||||||
|
|
||||||
|
if is_healthcheck_room(room.jid) then
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
|
||||||
|
go_live(room);
|
||||||
end);
|
end);
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -571,3 +670,22 @@ prosody.events.add_handler('pre-jitsi-authentication', function(session)
|
|||||||
return session.customusername;
|
return session.customusername;
|
||||||
end
|
end
|
||||||
end);
|
end);
|
||||||
|
|
||||||
|
-- when occupant is leaving breakout to join the main room and visitors are enabled
|
||||||
|
-- make sure we will allow that participant to join as it is already part of the main room
|
||||||
|
function handle_occupant_leaving_breakout(event)
|
||||||
|
local main_room, occupant, stanza = event.main_room, event.occupant, event.stanza;
|
||||||
|
local presence_status = stanza:get_child_text('status');
|
||||||
|
|
||||||
|
if presence_status ~= 'switch_room' or not visitors_promotion_map[main_room.jid] then
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
|
||||||
|
local node = jid.node(occupant.bare_jid);
|
||||||
|
|
||||||
|
visitors_promotion_map[main_room.jid][node] = {
|
||||||
|
from = 'none';
|
||||||
|
jid = occupant.bare_jid;
|
||||||
|
};
|
||||||
|
end
|
||||||
|
module:hook_global('jitsi-breakout-occupant-leaving', handle_occupant_leaving_breakout);
|
||||||
|
@ -80,7 +80,7 @@ function Util.new(module)
|
|||||||
These setups relay on configuration 'muc_domain_base' which holds
|
These setups relay on configuration 'muc_domain_base' which holds
|
||||||
the main domain and we use it to subtract subdomains from the
|
the main domain and we use it to subtract subdomains from the
|
||||||
virtual addresses.
|
virtual addresses.
|
||||||
The following confgurations are for multidomain setups and domain name
|
The following configurations are for multidomain setups and domain name
|
||||||
verification:
|
verification:
|
||||||
--]]
|
--]]
|
||||||
|
|
||||||
@ -225,7 +225,7 @@ function Util:get_public_key(keyId)
|
|||||||
self.cache:set(keyId, content);
|
self.cache:set(keyId, content);
|
||||||
else
|
else
|
||||||
if code == nil then
|
if code == nil then
|
||||||
-- this is timout after nr_retries retries
|
-- this is timeout after nr_retries retries
|
||||||
module:log('warn', 'Timeout retrieving %s from %s', keyId, keyurl);
|
module:log('warn', 'Timeout retrieving %s from %s', keyId, keyurl);
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
@ -1,7 +1,15 @@
|
|||||||
|
local http_server = require "net.http.server";
|
||||||
local jid = require "util.jid";
|
local jid = require "util.jid";
|
||||||
|
local st = require 'util.stanza';
|
||||||
local timer = require "util.timer";
|
local timer = require "util.timer";
|
||||||
local http = require "net.http";
|
local http = require "net.http";
|
||||||
local cache = require "util.cache";
|
local cache = require "util.cache";
|
||||||
|
local array = require "util.array";
|
||||||
|
local is_set = require 'util.set'.is_set;
|
||||||
|
local usermanager = require 'core.usermanager';
|
||||||
|
|
||||||
|
local config_global_admin_jids = module:context('*'):get_option_set('admins', {}) / jid.prep;
|
||||||
|
local config_admin_jids = module:get_option_inherited_set('admins', {}) / jid.prep;
|
||||||
|
|
||||||
local http_timeout = 30;
|
local http_timeout = 30;
|
||||||
local have_async, async = pcall(require, "util.async");
|
local have_async, async = pcall(require, "util.async");
|
||||||
@ -28,6 +36,8 @@ local roomless_iqs = {};
|
|||||||
|
|
||||||
local OUTBOUND_SIP_JIBRI_PREFIXES = { 'outbound-sip-jibri@', 'sipjibriouta@', 'sipjibrioutb@' };
|
local OUTBOUND_SIP_JIBRI_PREFIXES = { 'outbound-sip-jibri@', 'sipjibriouta@', 'sipjibrioutb@' };
|
||||||
local INBOUND_SIP_JIBRI_PREFIXES = { 'inbound-sip-jibri@', 'sipjibriina@', 'sipjibriina@' };
|
local INBOUND_SIP_JIBRI_PREFIXES = { 'inbound-sip-jibri@', 'sipjibriina@', 'sipjibriina@' };
|
||||||
|
local RECORDER_PREFIXES = module:get_option_inherited_set('recorder_prefixes', { 'recorder@recorder.', 'jibria@recorder.', 'jibrib@recorder.' });
|
||||||
|
local TRANSCRIBER_PREFIXES = module:get_option_inherited_set('transcriber_prefixes', { 'transcriber@recorder.', 'transcribera@recorder.', 'transcriberb@recorder.' });
|
||||||
|
|
||||||
local split_subdomain_cache = cache.new(1000);
|
local split_subdomain_cache = cache.new(1000);
|
||||||
local extract_subdomain_cache = cache.new(1000);
|
local extract_subdomain_cache = cache.new(1000);
|
||||||
@ -122,11 +132,7 @@ function get_room_from_jid(room_jid)
|
|||||||
local component = hosts[host];
|
local component = hosts[host];
|
||||||
if component then
|
if component then
|
||||||
local muc = component.modules.muc
|
local muc = component.modules.muc
|
||||||
if muc and rawget(muc,"rooms") then
|
if muc then
|
||||||
-- We're running 0.9.x or 0.10 (old MUC API)
|
|
||||||
return muc.rooms[room_jid];
|
|
||||||
elseif muc and rawget(muc,"get_room_from_jid") then
|
|
||||||
-- We're running >0.10 (new MUC API)
|
|
||||||
return muc.get_room_from_jid(room_jid);
|
return muc.get_room_from_jid(room_jid);
|
||||||
else
|
else
|
||||||
return
|
return
|
||||||
@ -202,8 +208,7 @@ end
|
|||||||
-- @param creator_group the group of the user who created the user which
|
-- @param creator_group the group of the user who created the user which
|
||||||
-- presence we are updating (this is the poltergeist case, where a user creates
|
-- presence we are updating (this is the poltergeist case, where a user creates
|
||||||
-- a poltergeist), optional.
|
-- a poltergeist), optional.
|
||||||
function update_presence_identity(
|
function update_presence_identity(stanza, user, group, creator_user, creator_group)
|
||||||
stanza, user, group, creator_user, creator_group)
|
|
||||||
|
|
||||||
-- First remove any 'identity' element if it already
|
-- First remove any 'identity' element if it already
|
||||||
-- exists, so it cannot be spoofed by a client
|
-- exists, so it cannot be spoofed by a client
|
||||||
@ -216,7 +221,11 @@ function update_presence_identity(
|
|||||||
end
|
end
|
||||||
return tag
|
return tag
|
||||||
end
|
end
|
||||||
)
|
);
|
||||||
|
|
||||||
|
if not user then
|
||||||
|
return;
|
||||||
|
end
|
||||||
|
|
||||||
stanza:tag("identity"):tag("user");
|
stanza:tag("identity"):tag("user");
|
||||||
for k, v in pairs(user) do
|
for k, v in pairs(user) do
|
||||||
@ -250,28 +259,36 @@ end
|
|||||||
-- Utility function to check whether feature is present and enabled. Allow
|
-- Utility function to check whether feature is present and enabled. Allow
|
||||||
-- a feature if there are features present in the session(coming from
|
-- a feature if there are features present in the session(coming from
|
||||||
-- the token) and the value of the feature is true.
|
-- the token) and the value of the feature is true.
|
||||||
-- If features is not present in the token we skip feature detection and allow
|
-- If features are missing but we have granted_features check that
|
||||||
-- everything.
|
-- if features are missing from the token we check whether it is moderator
|
||||||
function is_feature_allowed(features, ft)
|
function is_feature_allowed(ft, features, granted_features, is_moderator)
|
||||||
if (features == nil or features[ft] == "true" or features[ft] == true) then
|
if features then
|
||||||
return true;
|
return features[ft] == "true" or features[ft] == true;
|
||||||
|
elseif granted_features then
|
||||||
|
return granted_features[ft] == "true" or granted_features[ft] == true;
|
||||||
else
|
else
|
||||||
return false;
|
return is_moderator;
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Extracts the subdomain and room name from internal jid node [foo]room1
|
--- Extracts the subdomain and room name from internal jid node [foo]room1
|
||||||
-- @return subdomain(optional, if extracted or nil), the room name
|
-- @return subdomain(optional, if extracted or nil), the room name, the customer_id in case of vpaas
|
||||||
function extract_subdomain(room_node)
|
function extract_subdomain(room_node)
|
||||||
local ret = extract_subdomain_cache:get(room_node);
|
local ret = extract_subdomain_cache:get(room_node);
|
||||||
if ret then
|
if ret then
|
||||||
return ret.subdomain, ret.room;
|
return ret.subdomain, ret.room, ret.customer_id;
|
||||||
end
|
end
|
||||||
|
|
||||||
local subdomain, room_name = room_node:match("^%[([^%]]+)%](.+)$");
|
local subdomain, room_name = room_node:match("^%[([^%]]+)%](.+)$");
|
||||||
local cache_value = {subdomain=subdomain, room=room_name};
|
|
||||||
|
if not subdomain then
|
||||||
|
room_name = room_node;
|
||||||
|
end
|
||||||
|
|
||||||
|
local _, customer_id = subdomain and subdomain:match("^(vpaas%-magic%-cookie%-)(.*)$") or nil, nil;
|
||||||
|
local cache_value = { subdomain=subdomain, room=room_name, customer_id=customer_id };
|
||||||
extract_subdomain_cache:set(room_node, cache_value);
|
extract_subdomain_cache:set(room_node, cache_value);
|
||||||
return subdomain, room_name;
|
return subdomain, room_name, customer_id;
|
||||||
end
|
end
|
||||||
|
|
||||||
function starts_with(str, start)
|
function starts_with(str, start)
|
||||||
@ -282,19 +299,33 @@ function starts_with(str, start)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function starts_with_one_of(str, prefixes)
|
function starts_with_one_of(str, prefixes)
|
||||||
if not str then
|
if not str or not prefixes then
|
||||||
return false;
|
return false;
|
||||||
end
|
end
|
||||||
for i=1,#prefixes do
|
|
||||||
if starts_with(str, prefixes[i]) then
|
if is_set(prefixes) then
|
||||||
return prefixes[i];
|
-- set is a table with keys and value of true
|
||||||
|
for k, _ in prefixes:items() do
|
||||||
|
if starts_with(str, k) then
|
||||||
|
return k;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
for _, v in pairs(prefixes) do
|
||||||
|
if starts_with(str, v) then
|
||||||
|
return v;
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function ends_with(str, ending)
|
function ends_with(str, ending)
|
||||||
|
if not str then
|
||||||
|
return false;
|
||||||
|
end
|
||||||
|
|
||||||
return ending == "" or str:sub(-#ending) == ending
|
return ending == "" or str:sub(-#ending) == ending
|
||||||
end
|
end
|
||||||
|
|
||||||
@ -468,9 +499,38 @@ end
|
|||||||
|
|
||||||
-- Returns the initiator extension if the stanza is coming from a sip jigasi
|
-- Returns the initiator extension if the stanza is coming from a sip jigasi
|
||||||
function is_sip_jigasi(stanza)
|
function is_sip_jigasi(stanza)
|
||||||
|
if not stanza then
|
||||||
|
return false;
|
||||||
|
end
|
||||||
|
|
||||||
return stanza:get_child('initiator', 'http://jitsi.org/protocol/jigasi');
|
return stanza:get_child('initiator', 'http://jitsi.org/protocol/jigasi');
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- This requires presence stanza being passed
|
||||||
|
function is_transcriber_jigasi(stanza)
|
||||||
|
if not stanza then
|
||||||
|
return false;
|
||||||
|
end
|
||||||
|
|
||||||
|
local features = stanza:get_child('features');
|
||||||
|
if not features then
|
||||||
|
return false;
|
||||||
|
end
|
||||||
|
|
||||||
|
for i = 1, #features do
|
||||||
|
local feature = features[i];
|
||||||
|
if feature.attr and feature.attr.var and feature.attr.var == 'http://jitsi.org/protocol/transcriber' then
|
||||||
|
return true;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return false;
|
||||||
|
end
|
||||||
|
|
||||||
|
function is_transcriber(jid)
|
||||||
|
return starts_with_one_of(jid, TRANSCRIBER_PREFIXES);
|
||||||
|
end
|
||||||
|
|
||||||
function get_sip_jibri_email_prefix(email)
|
function get_sip_jibri_email_prefix(email)
|
||||||
if not email then
|
if not email then
|
||||||
return nil;
|
return nil;
|
||||||
@ -508,6 +568,10 @@ function is_sip_jibri_join(stanza)
|
|||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function is_jibri(occupant)
|
||||||
|
return starts_with_one_of(type(occupant) == "string" and occupant or occupant.jid, RECORDER_PREFIXES)
|
||||||
|
end
|
||||||
|
|
||||||
-- process a host module directly if loaded or hooks to wait for its load
|
-- process a host module directly if loaded or hooks to wait for its load
|
||||||
function process_host_module(name, callback)
|
function process_host_module(name, callback)
|
||||||
local function process_host(host)
|
local function process_host(host)
|
||||||
@ -535,30 +599,107 @@ function table_shallow_copy(t)
|
|||||||
return t2
|
return t2
|
||||||
end
|
end
|
||||||
|
|
||||||
|
local function table_find(tab, val)
|
||||||
|
if not tab then
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
for i, v in ipairs(tab) do
|
||||||
|
if v == val then
|
||||||
|
return i
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Adds second table values to the first table
|
||||||
|
local function table_add(t1, t2)
|
||||||
|
for _,v in ipairs(t2) do
|
||||||
|
table.insert(t1, v);
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Splits a string using delimiter
|
||||||
|
function split_string(str, delimiter)
|
||||||
|
str = str .. delimiter;
|
||||||
|
local result = array();
|
||||||
|
for w in str:gmatch("(.-)" .. delimiter) do
|
||||||
|
result:push(w);
|
||||||
|
end
|
||||||
|
|
||||||
|
return result;
|
||||||
|
end
|
||||||
|
|
||||||
|
-- send iq result that the iq was received and will be processed
|
||||||
|
function respond_iq_result(origin, stanza)
|
||||||
|
-- respond with successful receiving the iq
|
||||||
|
origin.send(st.iq({
|
||||||
|
type = 'result';
|
||||||
|
from = stanza.attr.to;
|
||||||
|
to = stanza.attr.from;
|
||||||
|
id = stanza.attr.id
|
||||||
|
}));
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Note: http_server.get_request_from_conn() was added in Prosody 0.12.3,
|
||||||
|
-- this code provides backwards compatibility with older versions
|
||||||
|
local get_request_from_conn = http_server.get_request_from_conn or function (conn)
|
||||||
|
local response = conn and conn._http_open_response;
|
||||||
|
return response and response.request or nil;
|
||||||
|
end;
|
||||||
|
|
||||||
|
-- Discover real remote IP of a session
|
||||||
|
function get_ip(session)
|
||||||
|
local request = get_request_from_conn(session.conn);
|
||||||
|
return request and request.ip or session.ip;
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Checks whether the provided jid is in the list of admins
|
||||||
|
-- we are not using the new permissions and roles api as we have few global modules which need to be
|
||||||
|
-- refactored into host modules, as that api needs to be executed in host context
|
||||||
|
local function is_admin(_jid)
|
||||||
|
local bare_jid = jid.bare(_jid);
|
||||||
|
|
||||||
|
if config_global_admin_jids:contains(bare_jid) or config_admin_jids:contains(bare_jid) then
|
||||||
|
return true;
|
||||||
|
end
|
||||||
|
return false;
|
||||||
|
end
|
||||||
|
|
||||||
return {
|
return {
|
||||||
OUTBOUND_SIP_JIBRI_PREFIXES = OUTBOUND_SIP_JIBRI_PREFIXES;
|
OUTBOUND_SIP_JIBRI_PREFIXES = OUTBOUND_SIP_JIBRI_PREFIXES;
|
||||||
INBOUND_SIP_JIBRI_PREFIXES = INBOUND_SIP_JIBRI_PREFIXES;
|
INBOUND_SIP_JIBRI_PREFIXES = INBOUND_SIP_JIBRI_PREFIXES;
|
||||||
|
RECORDER_PREFIXES = RECORDER_PREFIXES;
|
||||||
extract_subdomain = extract_subdomain;
|
extract_subdomain = extract_subdomain;
|
||||||
|
is_admin = is_admin;
|
||||||
is_feature_allowed = is_feature_allowed;
|
is_feature_allowed = is_feature_allowed;
|
||||||
|
is_jibri = is_jibri;
|
||||||
is_healthcheck_room = is_healthcheck_room;
|
is_healthcheck_room = is_healthcheck_room;
|
||||||
is_moderated = is_moderated;
|
is_moderated = is_moderated;
|
||||||
is_sip_jibri_join = is_sip_jibri_join;
|
is_sip_jibri_join = is_sip_jibri_join;
|
||||||
is_sip_jigasi = is_sip_jigasi;
|
is_sip_jigasi = is_sip_jigasi;
|
||||||
|
is_transcriber = is_transcriber;
|
||||||
|
is_transcriber_jigasi = is_transcriber_jigasi;
|
||||||
is_vpaas = is_vpaas;
|
is_vpaas = is_vpaas;
|
||||||
get_focus_occupant = get_focus_occupant;
|
get_focus_occupant = get_focus_occupant;
|
||||||
|
get_ip = get_ip;
|
||||||
get_room_from_jid = get_room_from_jid;
|
get_room_from_jid = get_room_from_jid;
|
||||||
get_room_by_name_and_subdomain = get_room_by_name_and_subdomain;
|
get_room_by_name_and_subdomain = get_room_by_name_and_subdomain;
|
||||||
get_sip_jibri_email_prefix = get_sip_jibri_email_prefix;
|
get_sip_jibri_email_prefix = get_sip_jibri_email_prefix;
|
||||||
async_handler_wrapper = async_handler_wrapper;
|
async_handler_wrapper = async_handler_wrapper;
|
||||||
presence_check_status = presence_check_status;
|
presence_check_status = presence_check_status;
|
||||||
process_host_module = process_host_module;
|
process_host_module = process_host_module;
|
||||||
|
respond_iq_result = respond_iq_result;
|
||||||
room_jid_match_rewrite = room_jid_match_rewrite;
|
room_jid_match_rewrite = room_jid_match_rewrite;
|
||||||
room_jid_split_subdomain = room_jid_split_subdomain;
|
room_jid_split_subdomain = room_jid_split_subdomain;
|
||||||
internal_room_jid_match_rewrite = internal_room_jid_match_rewrite;
|
internal_room_jid_match_rewrite = internal_room_jid_match_rewrite;
|
||||||
update_presence_identity = update_presence_identity;
|
update_presence_identity = update_presence_identity;
|
||||||
http_get_with_retry = http_get_with_retry;
|
http_get_with_retry = http_get_with_retry;
|
||||||
ends_with = ends_with;
|
ends_with = ends_with;
|
||||||
|
split_string = split_string;
|
||||||
starts_with = starts_with;
|
starts_with = starts_with;
|
||||||
starts_with_one_of = starts_with_one_of;
|
starts_with_one_of = starts_with_one_of;
|
||||||
|
table_add = table_add;
|
||||||
table_shallow_copy = table_shallow_copy;
|
table_shallow_copy = table_shallow_copy;
|
||||||
|
table_find = table_find;
|
||||||
};
|
};
|
||||||
|
@ -3,6 +3,7 @@ plugin_paths = { "{{ jitsi_root_dir }}/prosody/modules" }
|
|||||||
|
|
||||||
muc_mapper_domain_base = "{{ jitsi_domain }}";
|
muc_mapper_domain_base = "{{ jitsi_domain }}";
|
||||||
admins = { "{{ jitsi_jicofo_xmpp_user }}@{{ jitsi_auth_domain }}" };
|
admins = { "{{ jitsi_jicofo_xmpp_user }}@{{ jitsi_auth_domain }}" };
|
||||||
|
component_admins_as_room_owners = true;
|
||||||
http_default_host = "{{ jitsi_domain }}";
|
http_default_host = "{{ jitsi_domain }}";
|
||||||
|
|
||||||
-- Enable use of native prosody 0.11 support for epoll over select
|
-- Enable use of native prosody 0.11 support for epoll over select
|
||||||
|
@ -3,9 +3,9 @@
|
|||||||
jitsi_root_dir: /opt/jitsi
|
jitsi_root_dir: /opt/jitsi
|
||||||
jitsi_user: jitsi
|
jitsi_user: jitsi
|
||||||
|
|
||||||
jitsi_videobridge_version: "{{ jitsi_version | default('10133') }}"
|
jitsi_videobridge_version: "{{ jitsi_version | default('10314') }}"
|
||||||
jitsi_videobridge_archive_url: https://github.com/jitsi/jitsi-videobridge/archive/refs/tags/stable/jitsi-meet_{{ jitsi_videobridge_version }}.tar.gz
|
jitsi_videobridge_archive_url: https://github.com/jitsi/jitsi-videobridge/archive/refs/tags/stable/jitsi-meet_{{ jitsi_videobridge_version }}.tar.gz
|
||||||
jitsi_videobridge_archive_sha256: 79e7d50c3eb8c2528830b32ca8428235c02f6049f5601c4c0e7bfb772d980ec6
|
jitsi_videobridge_archive_sha256: 55ffb62de63c7280b4e2a6c25045da9c6f3e07c20f28d1b697510231ae56f8bf
|
||||||
|
|
||||||
jitsi_videobridge_rtp_port: 10000
|
jitsi_videobridge_rtp_port: 10000
|
||||||
jitsi_videobridge_src_ip:
|
jitsi_videobridge_src_ip:
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
[postgresql-client]
|
[postgresql-client]
|
||||||
baseurl = https://download.postgresql.org/pub/repos/yum/{{ repo_pg_client_version }}/redhat/rhel-{{ ansible_distribution_version }}-$basearch
|
baseurl = https://download.postgresql.org/pub/repos/yum/{{ repo_pg_client_version }}/redhat/rhel-{{ ansible_distribution_major_version }}-$basearch
|
||||||
gpgcheck = 1
|
gpgcheck = 1
|
||||||
gpgkey = https://download.postgresql.org/pub/repos/yum/keys/PGDG-RPM-GPG-KEY-RHEL
|
gpgkey = https://download.postgresql.org/pub/repos/yum/keys/PGDG-RPM-GPG-KEY-RHEL
|
||||||
name = PostgreSQL Client
|
name = PostgreSQL Client
|
||||||
|
Loading…
x
Reference in New Issue
Block a user