mirror of
https://git.lapiole.org/dani/ansible-roles.git
synced 2025-08-05 08:07:39 +02:00
Compare commits
19 Commits
72ddf7e5ea
...
master
Author | SHA1 | Date | |
---|---|---|---|
![]() |
e749b39e13 | ||
![]() |
58a1a78ce5 | ||
![]() |
cc8300da55 | ||
![]() |
1d891828f0 | ||
![]() |
0fcd8c4297 | ||
![]() |
65ebcfc093 | ||
![]() |
9c15069b74 | ||
![]() |
f8d3e57155 | ||
![]() |
3a08150ae4 | ||
![]() |
eb0714be6f | ||
![]() |
ffe9caded5 | ||
![]() |
9d979b11f2 | ||
![]() |
7c9556a76b | ||
![]() |
b6c1e1bbfd | ||
![]() |
23f39f1115 | ||
![]() |
04250e0ba7 | ||
![]() |
0aaa7d2625 | ||
![]() |
93beed0a89 | ||
![]() |
a5bfc87852 |
@@ -1,8 +1,8 @@
|
||||
---
|
||||
|
||||
# Version of consul to deploy
|
||||
consul_version: 1.20.5
|
||||
consul_version: 1.21.3
|
||||
# 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
|
||||
# Expected sha256 of the archive
|
||||
consul_archive_sha256: 75132816072b3c7da86f04153fc58fcfcf39abadee5279b3f72bec3cce01a16b
|
||||
consul_archive_sha256: ba20631037a5f63f70b0351c0875887a66c0a0d3feac2d255a768c9eb8c95e8b
|
||||
|
@@ -1,11 +1,11 @@
|
||||
---
|
||||
|
||||
# Version of consul-template to install
|
||||
consul_tpl_version: 0.40.0
|
||||
consul_tpl_version: 0.41.1
|
||||
# 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
|
||||
# Expected sha256 of the archive
|
||||
consul_tpl_archive_sha256: f73cb36988b9aaccb0ac918df26c854ccd199e60c0df011357405672f3d934bc
|
||||
consul_tpl_archive_sha256: ab68e09642437dcc5b6e9a572a1924d3969e4fe131f50a1a3a4f782d7a21f530
|
||||
|
||||
# Root dir where consul-template will be installed
|
||||
consul_tpl_root_dir: /opt/consul_template
|
||||
|
@@ -7,6 +7,11 @@ docker_base_conf:
|
||||
data-root: /opt/docker
|
||||
log-driver: journald
|
||||
storage-driver: overlay2
|
||||
default-ulimits:
|
||||
nofile:
|
||||
Hard: 1048576
|
||||
Soft: 1048576
|
||||
Name: nofile
|
||||
docker_extra_conf: {}
|
||||
# docker_extra_conf:
|
||||
# log-opts:
|
||||
|
@@ -1,11 +1,11 @@
|
||||
---
|
||||
|
||||
# Version to install
|
||||
gitea_version: '1.23.6'
|
||||
gitea_version: '1.24.3'
|
||||
# URL to the binary
|
||||
gitea_bin_url: https://dl.gitea.io/gitea/{{ gitea_version }}/gitea-{{ gitea_version }}-linux-amd64
|
||||
# sha256 of the binary
|
||||
gitea_bin_sha256: fcb76127fec7ba9fba10bfe11d81cdc01888aacb588fc4f29b124bf2ffba883e
|
||||
gitea_bin_sha256: a454176defd183788f2e1334834fbae7c16a62de142bd71ee4c9b01700e55f6d
|
||||
# Handle updates. If set to false, ansible will only install
|
||||
# Gitea and then won't touch an existing installation
|
||||
gitea_manage_upgrade: True
|
||||
|
@@ -162,4 +162,7 @@
|
||||
|
||||
- include_tasks: filebeat.yml
|
||||
tags: always
|
||||
|
||||
- include_tasks: vector.yml
|
||||
tags: always
|
||||
...
|
||||
|
5
roles/httpd_common/tasks/vector.yml
Normal file
5
roles/httpd_common/tasks/vector.yml
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
|
||||
- name: Deploy vector config
|
||||
template: src=vector.yml.j2 dest=/etc/vector/conf.d/httpd.yml
|
||||
tags: log,vector,web
|
21
roles/httpd_common/templates/vector.yml.j2
Normal file
21
roles/httpd_common/templates/vector.yml.j2
Normal file
@@ -0,0 +1,21 @@
|
||||
---
|
||||
|
||||
sources:
|
||||
in_logs_httpd:
|
||||
type: file
|
||||
include: ["/var/log/httpd/access_log", "/var/log/httpd/error_log"]
|
||||
|
||||
transforms:
|
||||
format_logs_httpd:
|
||||
type: remap
|
||||
inputs: ["in_logs_httpd"]
|
||||
source: |
|
||||
if (.file == "/var/log/httpd/access_log"){
|
||||
.http = parse_grok!(.message, "%{HOSTNAME:host} %{HTTPD_COMBINEDLOG}")
|
||||
}
|
||||
if (.file == "/var/log/httpd/error_log"){
|
||||
.http = parse_apache_log!(.message, format:"error")
|
||||
}
|
||||
.timestamp = parse_timestamp(del(.http.timestamp), format: "%d/%h/%Y:%H:%M:%S %z") ?? now()
|
||||
.service = "httpd"
|
||||
.group = "web"
|
@@ -9,16 +9,16 @@ jitsi_user: jitsi
|
||||
jitsi_web_src_ip:
|
||||
- 0.0.0.0/0
|
||||
|
||||
jitsi_version: 10133
|
||||
jitsi_version: 10431
|
||||
|
||||
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: 5a575f97a4f5f24b2406712cd25196083ac33535bde998d72bb0acf07d727592
|
||||
|
||||
# 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_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: 7402386e8ecb77ef3f6029b169aa07e6a56011d59ac0cb7019bfa1fdb3d8fcb3
|
||||
|
||||
jitsi_excalidraw_version: 2025.2.2
|
||||
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
|
||||
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
|
||||
|
||||
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
|
||||
-- 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;
|
||||
end
|
||||
end
|
||||
|
||||
-- in either case set auth_token in the session
|
||||
session.auth_token = token;
|
||||
session.user_agent_header = request.headers['user_agent'];
|
||||
end
|
||||
|
||||
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);
|
||||
if res == false then
|
||||
module:log("warn",
|
||||
"Error verifying token err:%s, reason:%s tenant:%s room:%s",
|
||||
error, reason, session.jitsi_web_query_prefix, session.jitsi_web_query_room);
|
||||
"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,
|
||||
session.user_agent_header);
|
||||
session.auth_token = nil;
|
||||
measure_verify_fail(1);
|
||||
return res, error, reason;
|
||||
|
@@ -1,6 +1,6 @@
|
||||
local avmoderation_component = module:get_option_string('av_moderation_component', 'avmoderation.'..module.host);
|
||||
-- TODO: Remove this file after several stable releases when people update their configs
|
||||
module:log('warn', 'mod_av_moderation is deprecated and will be removed in a future release. '
|
||||
.. 'Please update your config by removing this module from the list of loaded modules.');
|
||||
|
||||
-- Advertise AV Moderation so client can pick up the address and use it
|
||||
module:add_identity('component', 'av_moderation', avmoderation_component);
|
||||
|
||||
module:depends("jitsi_session");
|
||||
module:depends('jitsi_session');
|
||||
module:depends('features_identity');
|
||||
|
@@ -4,6 +4,8 @@ local is_healthcheck_room = util.is_healthcheck_room;
|
||||
local internal_room_jid_match_rewrite = util.internal_room_jid_match_rewrite;
|
||||
local room_jid_match_rewrite = util.room_jid_match_rewrite;
|
||||
local process_host_module = util.process_host_module;
|
||||
local table_shallow_copy = util.table_shallow_copy;
|
||||
local is_admin = util.is_admin;
|
||||
local array = require "util.array";
|
||||
local json = require 'cjson.safe';
|
||||
local st = require 'util.stanza';
|
||||
@@ -14,6 +16,12 @@ if muc_component_host == nil then
|
||||
return;
|
||||
end
|
||||
|
||||
local main_virtual_host = module:get_option_string('muc_mapper_domain_base');
|
||||
if not main_virtual_host then
|
||||
module:log('warn', 'No "muc_mapper_domain_base" option set, disabling AV moderation.');
|
||||
return ;
|
||||
end
|
||||
|
||||
module:log('info', 'Starting av_moderation for %s', muc_component_host);
|
||||
|
||||
-- Returns the index of the given element in the table
|
||||
@@ -47,7 +55,7 @@ function notify_occupants_enable(jid, enable, room, actorJid, mediaType)
|
||||
body_json.type = 'av_moderation';
|
||||
body_json.enabled = enable;
|
||||
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;
|
||||
local body_json_str, error = json.encode(body_json);
|
||||
|
||||
@@ -75,11 +83,20 @@ function notify_whitelist_change(jid, moderators, room, mediaType, removed)
|
||||
local body_json = {};
|
||||
body_json.type = 'av_moderation';
|
||||
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
|
||||
body_json.removed = true;
|
||||
end
|
||||
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', 'desktop'}) 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);
|
||||
|
||||
if not moderators_body_json_str then
|
||||
@@ -135,6 +152,36 @@ function notify_jid_approved(jid, from, room, mediaType)
|
||||
send_json_message(jid, json_message);
|
||||
end
|
||||
|
||||
function start_av_moderation(room, mediaType, occupant)
|
||||
if not room.av_moderation then
|
||||
room.av_moderation = {};
|
||||
room.av_moderation_actors = {};
|
||||
end
|
||||
room.av_moderation[mediaType] = array();
|
||||
|
||||
-- add all current moderators to the new whitelist
|
||||
for _, room_occupant in room:each_occupant() do
|
||||
if room_occupant.role == 'moderator' and not ends_with(room_occupant.nick, '/focus') then
|
||||
room.av_moderation[mediaType]:push(internal_room_jid_match_rewrite(room_occupant.nick));
|
||||
end
|
||||
end
|
||||
|
||||
-- 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[mediaType] = 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;
|
||||
end
|
||||
|
||||
-- receives messages from clients to the component sending A/V moderation enable/disable commands or adding
|
||||
-- jids to the whitelist
|
||||
function on_message(event)
|
||||
@@ -175,7 +222,7 @@ function on_message(event)
|
||||
|
||||
local mediaType = moderation_command.attr.mediaType;
|
||||
if mediaType then
|
||||
if mediaType ~= 'audio' and mediaType ~= 'video' then
|
||||
if mediaType ~= 'audio' and mediaType ~= 'video' and mediaType ~= 'desktop' then
|
||||
module:log('warn', 'Wrong mediaType %s for %s', mediaType, room.jid);
|
||||
return false;
|
||||
end
|
||||
@@ -192,12 +239,7 @@ function on_message(event)
|
||||
module:log('warn', 'Concurrent moderator enable/disable request or something is out of sync');
|
||||
return true;
|
||||
else
|
||||
if not room.av_moderation then
|
||||
room.av_moderation = {};
|
||||
room.av_moderation_actors = {};
|
||||
end
|
||||
room.av_moderation[mediaType] = array{};
|
||||
room.av_moderation_actors[mediaType] = occupant.nick;
|
||||
start_av_moderation(room, mediaType, occupant);
|
||||
end
|
||||
else
|
||||
enabled = false;
|
||||
@@ -208,7 +250,11 @@ function on_message(event)
|
||||
room.av_moderation[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;
|
||||
for key,_ in pairs(room.av_moderation) do
|
||||
if room.av_moderation[key] then
|
||||
@@ -223,6 +269,12 @@ function on_message(event)
|
||||
|
||||
-- send message to all occupants
|
||||
notify_occupants_enable(nil, enabled, room, occupant.nick, mediaType);
|
||||
|
||||
if enabled then
|
||||
-- inform all moderators for the newly created whitelist
|
||||
notify_whitelist_change(nil, true, room, mediaType);
|
||||
end
|
||||
|
||||
return true;
|
||||
elseif moderation_command.attr.jidToWhitelist then
|
||||
local occupant_jid = moderation_command.attr.jidToWhitelist;
|
||||
@@ -283,12 +335,32 @@ end
|
||||
function occupant_joined(event)
|
||||
local room, occupant = event.room, event.occupant;
|
||||
|
||||
if is_healthcheck_room(room.jid) then
|
||||
if is_healthcheck_room(room.jid) or is_admin(occupant.bare_jid) then
|
||||
return;
|
||||
end
|
||||
|
||||
-- when first moderator joins if av_can_unmute from password preset is set to false, we enable av moderation for both
|
||||
-- audio and video, and set the first moderator as the actor that enabled it
|
||||
if room._data.av_can_unmute ~= nil
|
||||
and not room._data.av_first_moderator_joined
|
||||
|
||||
-- occupant.role is not reflecting the actual role after set_affiliation is used in same occupant_joined event
|
||||
and room:get_role(occupant.nick) == 'moderator' then
|
||||
|
||||
if not room._data.av_can_unmute then
|
||||
for _,mediaType in pairs({'audio', 'video', 'desktop'}) do
|
||||
start_av_moderation(room, mediaType, occupant);
|
||||
|
||||
notify_occupants_enable(nil, true, room, occupant.nick, mediaType);
|
||||
end
|
||||
|
||||
room._data.av_first_moderator_joined = true;
|
||||
return;
|
||||
end
|
||||
end
|
||||
|
||||
if room.av_moderation then
|
||||
for _,mediaType in pairs({'audio', 'video'}) do
|
||||
for _,mediaType in pairs({'audio', 'video', 'desktop'}) do
|
||||
if room.av_moderation[mediaType] then
|
||||
notify_occupants_enable(
|
||||
occupant.jid, true, room, room.av_moderation_actors[mediaType], mediaType);
|
||||
@@ -298,9 +370,13 @@ function occupant_joined(event)
|
||||
-- NOTE for some reason event.occupant.role is not reflecting the actual occupant role (when changed
|
||||
-- from allowners module) but iterating over room occupants returns the correct role
|
||||
for _, room_occupant in room:each_occupant() do
|
||||
-- if moderator send the whitelist
|
||||
if room_occupant.nick == occupant.nick and room_occupant.role == 'moderator' then
|
||||
notify_whitelist_change(room_occupant.jid, false, room);
|
||||
-- if it is a moderator, send the whitelist to every moderator
|
||||
if room_occupant.nick == occupant.nick and room_occupant.role == 'moderator' then
|
||||
for _,mediaType in pairs({'audio', 'video', 'desktop'}) do
|
||||
if room.av_moderation[mediaType] then
|
||||
notify_whitelist_change(nil, true, room, mediaType);
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -308,14 +384,30 @@ end
|
||||
|
||||
-- when a occupant was granted moderator we need to update him with the whitelist
|
||||
function occupant_affiliation_changed(event)
|
||||
local room = event.room;
|
||||
if not room.av_moderation or is_healthcheck_room(room.jid) or is_admin(event.jid)
|
||||
or event.affiliation ~= 'owner' then
|
||||
return;
|
||||
end
|
||||
|
||||
-- in any enabled media type add the new moderator to the whitelist
|
||||
for _, room_occupant in room:each_occupant() do
|
||||
if room_occupant.bare_jid == event.jid then
|
||||
for _,mediaType in pairs({'audio', 'video', 'desktop'}) do
|
||||
if room.av_moderation[mediaType] then
|
||||
room.av_moderation[mediaType]:push(internal_room_jid_match_rewrite(room_occupant.nick));
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- the actor can be nil if is coming from allowners or similar module we want to skip it here
|
||||
-- as we will handle it in occupant_joined
|
||||
if event.actor and event.affiliation == 'owner' and event.room.av_moderation then
|
||||
local room = event.room;
|
||||
-- event.jid is the bare jid of participant
|
||||
for _, occupant in room:each_occupant() do
|
||||
if occupant.bare_jid == event.jid then
|
||||
notify_whitelist_change(occupant.jid, false, room);
|
||||
if event.actor and event.affiliation == 'owner' then
|
||||
-- notify all moderators for the new grant moderator and the change in whitelists
|
||||
for _,mediaType in pairs({'audio', 'video', 'desktop'}) do
|
||||
if room.av_moderation[mediaType] then
|
||||
notify_whitelist_change(nil, true, room, mediaType);
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -329,3 +421,9 @@ process_host_module(muc_component_host, function(host_module, host)
|
||||
host_module:hook('muc-occupant-joined', occupant_joined, -2); -- make sure it runs after allowners or similar
|
||||
host_module:hook('muc-set-affiliation', occupant_affiliation_changed, -1);
|
||||
end);
|
||||
|
||||
process_host_module(main_virtual_host, function(host_module)
|
||||
module:context(host_module.host):fire_event('jitsi-add-identity', {
|
||||
name = 'av_moderation'; host = module.host;
|
||||
});
|
||||
end);
|
||||
|
@@ -1,24 +1,21 @@
|
||||
-- This module is added under the main virtual host domain
|
||||
--
|
||||
-- VirtualHost "jitmeet.example.com"
|
||||
-- modules_enabled = {
|
||||
-- "end_conference"
|
||||
-- }
|
||||
-- end_conference_component = "endconference.jitmeet.example.com"
|
||||
--
|
||||
-- Component "endconference.jitmeet.example.com" "end_conference"
|
||||
-- muc_component = muc.jitmeet.example.com
|
||||
--
|
||||
local get_room_by_name_and_subdomain = module:require 'util'.get_room_by_name_and_subdomain;
|
||||
local util = module:require 'util';
|
||||
local get_room_by_name_and_subdomain = util.get_room_by_name_and_subdomain;
|
||||
local process_host_module = util.process_host_module;
|
||||
|
||||
local END_CONFERENCE_REASON = 'The meeting has been terminated';
|
||||
|
||||
-- Since this file serves as both the host module and the component, we rely on the assumption that
|
||||
-- end_conference_component var would only be define for the host and not in the end_conference component
|
||||
-- TODO: Remove this if block after several stable releases when people update their configs
|
||||
local end_conference_component = module:get_option_string('end_conference_component');
|
||||
if end_conference_component then
|
||||
-- Advertise end conference so client can pick up the address and use it
|
||||
module:add_identity('component', 'end_conference', end_conference_component);
|
||||
module:log('warn', 'Please update your config by removing muc_end_conference module from '
|
||||
.. 'the list of loaded modules in the main virtual host.');
|
||||
module:depends("features_identity");
|
||||
return; -- nothing left to do if called as host module
|
||||
end
|
||||
|
||||
@@ -32,6 +29,12 @@ if muc_component_host == nil then
|
||||
return;
|
||||
end
|
||||
|
||||
local main_virtual_host = module:get_option_string('muc_mapper_domain_base');
|
||||
if not main_virtual_host then
|
||||
module:log('warn', 'No "muc_mapper_domain_base" option set, disabling end conference component.');
|
||||
return ;
|
||||
end
|
||||
|
||||
module:log('info', 'Starting end_conference for %s', muc_component_host);
|
||||
|
||||
-- receives messages from clients to the component to end a conference
|
||||
@@ -84,3 +87,9 @@ end
|
||||
|
||||
-- we will receive messages from the clients
|
||||
module:hook('message/host', on_message);
|
||||
|
||||
process_host_module(main_virtual_host, function(host_module)
|
||||
module:context(host_module.host):fire_event('jitsi-add-identity', {
|
||||
name = 'end_conference'; host = module.host;
|
||||
});
|
||||
end);
|
||||
|
@@ -0,0 +1,8 @@
|
||||
-- Other components can use the event 'jitsi-add-identity' to attach identity which
|
||||
-- will be advertised by the main virtual host and discovered by clients.
|
||||
-- With this we avoid having an almost empty module to just add identity with an extra config
|
||||
|
||||
module:hook('jitsi-add-identity', function(event)
|
||||
module:log('info', 'Adding identity %s for host %s', event.name, event.host);
|
||||
module:add_identity('component', event.name, event.host);
|
||||
end);
|
245
roles/jitsi/files/prosody/modules/mod_filesharing_component.lua
Normal file
245
roles/jitsi/files/prosody/modules/mod_filesharing_component.lua
Normal file
@@ -0,0 +1,245 @@
|
||||
local json = require 'cjson.safe';
|
||||
local jid = require 'util.jid';
|
||||
local st = require 'util.stanza';
|
||||
|
||||
local util = module:require 'util';
|
||||
local internal_room_jid_match_rewrite = util.internal_room_jid_match_rewrite;
|
||||
local is_admin = util.is_admin;
|
||||
local process_host_module = util.process_host_module;
|
||||
|
||||
local FILE_SHARING_IDENTITY_TYPE = 'file-sharing';
|
||||
local JSON_TYPE_ADD_FILE = 'add';
|
||||
local JSON_TYPE_REMOVE_FILE = 'remove';
|
||||
local JSON_TYPE_LIST_FILES = 'list';
|
||||
local NICK_NS = 'http://jabber.org/protocol/nick';
|
||||
|
||||
-- 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;
|
||||
|
||||
local muc_component_host = module:get_option_string('muc_component');
|
||||
if muc_component_host == nil then
|
||||
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, disabling file sharing component.");
|
||||
return ;
|
||||
end
|
||||
|
||||
-- receives messages from clients to the component sending file sharing commands for adding or removing files
|
||||
function on_message(event)
|
||||
local session, stanza = event.origin, event.stanza;
|
||||
|
||||
-- Check the type of the incoming stanza to avoid loops:
|
||||
if stanza.attr.type == 'error' then
|
||||
return; -- We do not want to reply to these, so leave.
|
||||
end
|
||||
|
||||
if not session or not session.jitsi_web_query_room then
|
||||
return false;
|
||||
end
|
||||
|
||||
local message = stanza:get_child(FILE_SHARING_IDENTITY_TYPE, 'http://jitsi.org/jitmeet');
|
||||
|
||||
if not message then
|
||||
return false;
|
||||
end
|
||||
|
||||
-- get room name with tenant and find room
|
||||
local room = get_room_by_name_and_subdomain(session.jitsi_web_query_room, session.jitsi_web_query_prefix);
|
||||
|
||||
if not room then
|
||||
module:log('warn', 'No room found for %s/%s', session.jitsi_web_query_prefix, session.jitsi_web_query_room);
|
||||
return false;
|
||||
end
|
||||
|
||||
-- check that the participant sending the message is an occupant in the room
|
||||
local from = stanza.attr.from;
|
||||
local occupant = room:get_occupant_by_real_jid(from);
|
||||
|
||||
if not occupant then
|
||||
module:log('warn', 'No occupant %s found for %s', from, room.jid);
|
||||
return false;
|
||||
end
|
||||
|
||||
if not is_feature_allowed(
|
||||
'file-upload',
|
||||
session.jitsi_meet_context_features,
|
||||
room:get_affiliation(stanza.attr.from) == 'owner') then
|
||||
session.send(st.error_reply(stanza, 'auth', 'forbidden'));
|
||||
return true;
|
||||
end
|
||||
|
||||
if message.attr.type == JSON_TYPE_ADD_FILE then
|
||||
local msg_obj, error = json.decode(message:get_text());
|
||||
|
||||
if error then
|
||||
module:log('error','Error decoding data error:%s %s', error, stanza);
|
||||
return false;
|
||||
end
|
||||
|
||||
if not msg_obj.fileId then
|
||||
module:log('error', 'Error missing required field: %s', stanza);
|
||||
return false;
|
||||
end
|
||||
|
||||
-- make sure we overwrite data for sender so we avoid spoofing
|
||||
msg_obj.authorParticipantId = jid.resource(occupant.nick);
|
||||
msg_obj.authorParticipantJid = from;
|
||||
|
||||
local nick_element = occupant:get_presence():get_child('nick', NICK_NS);
|
||||
if nick_element then
|
||||
msg_obj.authorParticipantName = nick_element:get_text();
|
||||
else
|
||||
msg_obj.authorParticipantName = 'anonymous';
|
||||
end
|
||||
msg_obj.conferenceFullName = internal_room_jid_match_rewrite(room.jid);
|
||||
|
||||
module:context(muc_domain_base):fire_event('jitsi-filesharing-add', {
|
||||
room = room; file = msg_obj; actor = occupant.nick;
|
||||
});
|
||||
|
||||
module:context(muc_domain_base):fire_event('jitsi-filesharing-updated', {
|
||||
room = room;
|
||||
});
|
||||
|
||||
return true;
|
||||
elseif message.attr.type == JSON_TYPE_REMOVE_FILE then
|
||||
if not message.attr.fileId then
|
||||
module:log('error', 'Error missing required field: %s', stanza);
|
||||
return true;
|
||||
end
|
||||
|
||||
module:context(muc_domain_base):fire_event('jitsi-filesharing-remove', {
|
||||
room = room; id = message.attr.fileId; actor = occupant.nick;
|
||||
});
|
||||
|
||||
module:context(muc_domain_base):fire_event('jitsi-filesharing-updated', {
|
||||
room = room;
|
||||
});
|
||||
|
||||
return true;
|
||||
else
|
||||
-- return error.
|
||||
return false;
|
||||
end
|
||||
end
|
||||
|
||||
-- handles new occupants to inform them about any file shared by other participants
|
||||
function occupant_joined(event)
|
||||
local room, occupant = event.room, event.occupant;
|
||||
|
||||
-- healthcheck rooms does not have shared files
|
||||
if not room.jitsi_shared_files
|
||||
or is_admin(occupant.bare_jid)
|
||||
or not room.jitsi_shared_files
|
||||
or next(room.jitsi_shared_files) == nil then
|
||||
return;
|
||||
end
|
||||
|
||||
|
||||
-- send file list to the new occupant
|
||||
local json_msg, error = json.encode({
|
||||
type = FILE_SHARING_IDENTITY_TYPE,
|
||||
event = JSON_TYPE_LIST_FILES,
|
||||
files = room.jitsi_shared_files
|
||||
});
|
||||
|
||||
local stanza = st.message({ from = module.host; to = occupant.jid; })
|
||||
:tag('json-message', { xmlns = 'http://jitsi.org/jitmeet' })
|
||||
:text(json_msg):up();
|
||||
|
||||
module:send(stanza);
|
||||
end
|
||||
|
||||
process_host_module(muc_component_host, function(host_module, host)
|
||||
module:log('info','Hook to muc events on %s', host);
|
||||
host_module:hook('muc-occupant-joined', occupant_joined, -10); -- make sure it runs after allowners or similar
|
||||
end);
|
||||
|
||||
-- we will receive messages from the clients
|
||||
module:hook('message/host', on_message);
|
||||
|
||||
process_host_module(muc_domain_base, function(host_module, host)
|
||||
module:context(muc_domain_base):fire_event('jitsi-add-identity', {
|
||||
name = FILE_SHARING_IDENTITY_TYPE; host = module.host;
|
||||
});
|
||||
module:context(muc_domain_base):hook('jitsi-filesharing-add', function(event)
|
||||
local actor, file, room = event.actor, event.file, event.room;
|
||||
|
||||
if not room.jitsi_shared_files then
|
||||
room.jitsi_shared_files = {};
|
||||
end
|
||||
|
||||
room.jitsi_shared_files[file.fileId] = file;
|
||||
|
||||
local json_msg, error = json.encode({
|
||||
type = FILE_SHARING_IDENTITY_TYPE,
|
||||
event = JSON_TYPE_ADD_FILE,
|
||||
file = file
|
||||
});
|
||||
|
||||
if not json_msg then
|
||||
module:log('error', 'skip sending add request room:%s error:%s', room.jid, error);
|
||||
return false
|
||||
end
|
||||
|
||||
local stanza = st.message({ from = module.host; }):tag('json-message', { xmlns = 'http://jitsi.org/jitmeet' })
|
||||
:text(json_msg):up();
|
||||
|
||||
-- send add file to all occupants except jicofo and sender
|
||||
-- if this is visitor prosody send it only to visitors
|
||||
for _, room_occupant in room:each_occupant() do
|
||||
local send_event = not is_admin(room_occupant.bare_jid) and room_occupant.nick ~= actor;
|
||||
if is_visitor_prosody then
|
||||
send_event = room_occupant.role == 'visitor';
|
||||
end
|
||||
if send_event then
|
||||
local to_send = st.clone(stanza);
|
||||
to_send.attr.to = room_occupant.jid;
|
||||
module:send(to_send);
|
||||
end
|
||||
end
|
||||
end);
|
||||
module:context(muc_domain_base):hook('jitsi-filesharing-remove', function(event)
|
||||
local actor, id, room = event.actor, event.id, event.room;
|
||||
|
||||
if not room.jitsi_shared_files then
|
||||
return;
|
||||
end
|
||||
|
||||
room.jitsi_shared_files[id] = nil;
|
||||
|
||||
local json_msg, error = json.encode({
|
||||
type = FILE_SHARING_IDENTITY_TYPE,
|
||||
event = JSON_TYPE_REMOVE_FILE,
|
||||
fileId = id
|
||||
});
|
||||
|
||||
if not json_msg then
|
||||
module:log('error', 'skip sending remove request room:%s error:%s', room.jid, error);
|
||||
return false
|
||||
end
|
||||
|
||||
local stanza = st.message({ from = module.host; }):tag('json-message', { xmlns = 'http://jitsi.org/jitmeet' })
|
||||
:text(json_msg):up();
|
||||
|
||||
-- send remove file to all occupants except jicofo and sender
|
||||
-- if this is visitor prosody send it only to visitors
|
||||
for _, room_occupant in room:each_occupant() do
|
||||
local send_event = not is_admin(room_occupant.bare_jid) and room_occupant.nick ~= actor;
|
||||
if is_visitor_prosody then
|
||||
send_event = room_occupant.role == 'visitor';
|
||||
end
|
||||
if send_event then
|
||||
local to_send = st.clone(stanza);
|
||||
to_send.attr.to = room_occupant.jid;
|
||||
module:send(to_send);
|
||||
end
|
||||
end
|
||||
end);
|
||||
end);
|
@@ -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 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
|
||||
-- has features in the user context, but without feature for recording
|
||||
@@ -10,15 +32,34 @@ module:hook("pre-iq/full", function(event)
|
||||
if jibri then
|
||||
local session = event.origin;
|
||||
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,
|
||||
occupant.role == 'moderator');
|
||||
|
||||
if jibri.attr.action == 'start' then
|
||||
if token == nil
|
||||
or not is_feature_allowed(session.jitsi_meet_context_features,
|
||||
(jibri.attr.recording_mode == 'file' and 'recording' or 'livestreaming')
|
||||
) then
|
||||
module:log("info",
|
||||
"Filtering jibri start recording, stanza:%s", tostring(stanza));
|
||||
session.send(st.error_reply(stanza, "auth", "forbidden"));
|
||||
if jibri.attr.action == 'start' or jibri.attr.action == 'stop' then
|
||||
if not is_allowed then
|
||||
module:log('info', 'Filtering jibri start recording, stanza:%s', tostring(stanza));
|
||||
session.send(st.error_reply(stanza, 'auth', 'forbidden'));
|
||||
return true;
|
||||
end
|
||||
|
||||
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;
|
||||
end
|
||||
end
|
||||
|
@@ -1,13 +1,14 @@
|
||||
-- This module is enabled under the main virtual host
|
||||
local new_throttle = require "util.throttle".create;
|
||||
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 is_admin = util.is_admin;
|
||||
local room_jid_match_rewrite = util.room_jid_match_rewrite;
|
||||
local is_feature_allowed = util.is_feature_allowed;
|
||||
local is_sip_jigasi = util.is_sip_jigasi;
|
||||
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 jid_bare = require "util.jid".bare;
|
||||
|
||||
@@ -22,17 +23,30 @@ if main_muc_component_host == nil then
|
||||
end
|
||||
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
|
||||
if token_util == nil then
|
||||
module:log("error", "no token configuration but it is required");
|
||||
return;
|
||||
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,
|
||||
-- and also the maximum number of new calls per minute that a session is allowed to create.
|
||||
local limit_outgoing_calls;
|
||||
@@ -44,6 +58,8 @@ load_config();
|
||||
-- Header names to use to push extra data extracted from token, if any
|
||||
local OUT_INITIATOR_USER_ATTR_NAME = "X-outbound-call-initiator-user";
|
||||
local OUT_INITIATOR_GROUP_ATTR_NAME = "X-outbound-call-initiator-group";
|
||||
local OUT_ROOM_NAME_ATTR_NAME = "JvbRoomName";
|
||||
|
||||
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
|
||||
|
||||
@@ -59,30 +75,44 @@ module:hook("pre-iq/full", function(event)
|
||||
local token = session.auth_token;
|
||||
|
||||
-- find header with attr name 'JvbRoomName' and extract its value
|
||||
local headerName = 'JvbRoomName';
|
||||
local roomName;
|
||||
for _, child in ipairs(dial.tags) do
|
||||
if (child.name == 'header'
|
||||
and child.attr.name == headerName) then
|
||||
roomName = child.attr.value;
|
||||
break;
|
||||
-- Remove any 'header' element if it already exists, so it cannot be spoofed by a client
|
||||
dial: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
|
||||
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
|
||||
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
|
||||
|
||||
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,
|
||||
room:get_affiliation(stanza.attr.from) == 'owner');
|
||||
|
||||
-- if current user is not allowed, but was granted moderation by a user
|
||||
-- that is allowed by its features we want to allow it
|
||||
local is_granting_session_allowed = false;
|
||||
if (session.granted_jitsi_meet_context_features) then
|
||||
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))
|
||||
if roomName == nil
|
||||
or roomName ~= room_jid
|
||||
or (token ~= nil and not token_util:verify_room(session, room_real_jid))
|
||||
or not is_session_allowed
|
||||
then
|
||||
module:log("warn", "Filtering stanza dial, stanza:%s", tostring(stanza));
|
||||
session.send(st.error_reply(stanza, "auth", "forbidden"));
|
||||
@@ -99,8 +129,8 @@ module:hook("pre-iq/full", function(event)
|
||||
group_id = session.granted_jitsi_meet_context_group_id;
|
||||
end
|
||||
|
||||
-- now lets check any limits if configured
|
||||
if limit_outgoing_calls > 0 then
|
||||
-- now lets check any limits for outgoing calls if configured
|
||||
if feature == 'outbound-call' and limit_outgoing_calls > 0 then
|
||||
if not session.dial_out_throttle then
|
||||
-- module:log("debug", "Enabling dial-out throttle session=%s.", session);
|
||||
session.dial_out_throttle = new_throttle(limit_outgoing_calls, OUTGOING_CALLS_THROTTLE_INTERVAL);
|
||||
@@ -119,25 +149,11 @@ module:hook("pre-iq/full", function(event)
|
||||
|
||||
-- now lets insert token information if any
|
||||
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
|
||||
dial:tag("header", {
|
||||
xmlns = "urn:xmpp:rayo:1",
|
||||
name = OUT_INITIATOR_USER_ATTR_NAME,
|
||||
value = user_id });
|
||||
value = tostring(user_id)});
|
||||
dial:up();
|
||||
|
||||
-- Add the initiator group information if it is present
|
||||
@@ -145,13 +161,22 @@ module:hook("pre-iq/full", function(event)
|
||||
dial:tag("header", {
|
||||
xmlns = "urn:xmpp:rayo:1",
|
||||
name = OUT_INITIATOR_GROUP_ATTR_NAME,
|
||||
value = session.jitsi_meet_context_group });
|
||||
value = tostring(session.jitsi_meet_context_group) });
|
||||
dial:up();
|
||||
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, 1); -- make sure we run before domain mapper
|
||||
|
||||
--- Finds and returns the number of concurrent outgoing calls for a user
|
||||
-- @param context_user the user id extracted from the token
|
||||
@@ -200,49 +225,10 @@ end
|
||||
|
||||
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)
|
||||
module:log('debug', 'Main muc loaded');
|
||||
|
||||
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
|
||||
|
||||
process_host_module(main_muc_component_host, function(host_module, host)
|
||||
@@ -259,3 +245,34 @@ process_host_module(main_muc_component_host, function(host_module, host)
|
||||
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,12 @@ local jid = require 'util.jid';
|
||||
local st = require 'util.stanza';
|
||||
local new_id = require 'util.id'.medium;
|
||||
local filters = require 'util.filters';
|
||||
local array = require 'util.array';
|
||||
local set = require 'util.set';
|
||||
local json = require 'cjson.safe';
|
||||
|
||||
local util = module:require 'util';
|
||||
local is_admin = util.is_admin;
|
||||
local ends_with = util.ends_with;
|
||||
local is_vpaas = util.is_vpaas;
|
||||
local room_jid_match_rewrite = util.room_jid_match_rewrite;
|
||||
@@ -23,6 +27,13 @@ local get_room_from_jid = util.get_room_from_jid;
|
||||
local get_focus_occupant = util.get_focus_occupant;
|
||||
local internal_room_jid_match_rewrite = util.internal_room_jid_match_rewrite;
|
||||
local presence_check_status = util.presence_check_status;
|
||||
local respond_iq_result = util.respond_iq_result;
|
||||
local table_compare = util.table_compare;
|
||||
|
||||
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
|
||||
local local_domain = module:get_option_string('muc_mapper_domain_base');
|
||||
@@ -43,6 +54,9 @@ local local_muc_domain = muc_domain_prefix..'.'..local_domain;
|
||||
|
||||
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
|
||||
local measure_rooms = module:measure('vnode-rooms', 'amount');
|
||||
local measure_participants = module:measure('vnode-participants', 'amount');
|
||||
@@ -52,17 +66,72 @@ local sent_iq_cache = require 'util.cache'.new(200);
|
||||
|
||||
local sessions = prosody.full_sessions;
|
||||
|
||||
local um_is_admin = require 'core.usermanager'.is_admin;
|
||||
local function is_admin(jid)
|
||||
return um_is_admin(jid, module.host);
|
||||
local function send_transcriptions_update(room)
|
||||
-- let's notify main prosody
|
||||
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
|
||||
|
||||
-- mark all occupants as visitors
|
||||
module:hook('muc-occupant-pre-join', function (event)
|
||||
local occupant, room, origin, stanza = event.occupant, event.room, event.origin, event.stanza;
|
||||
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
|
||||
origin.send(st.error_reply(stanza, 'cancel', 'not-allowed', 'Visitors not allowed while lobby is on!')
|
||||
:tag('no-visitors-lobby', { xmlns = 'jitsi:visitors' }));
|
||||
@@ -70,6 +139,9 @@ module:hook('muc-occupant-pre-join', function (event)
|
||||
else
|
||||
occupant.role = 'visitor';
|
||||
end
|
||||
elseif room.moderators_list and room.moderators_list:contains(resource) then
|
||||
-- remote participants, host is the main prosody
|
||||
occupant.role = 'moderator';
|
||||
end
|
||||
end, 3);
|
||||
|
||||
@@ -89,7 +161,7 @@ module:hook('muc-occupant-pre-leave', function (event)
|
||||
-- to main prosody
|
||||
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
|
||||
if raiseHand and #raiseHand > 0 then
|
||||
@@ -111,6 +183,7 @@ module:hook('muc-occupant-pre-leave', function (event)
|
||||
module:send(promotion_request);
|
||||
end
|
||||
|
||||
remove_transcription(room, occupant);
|
||||
end, 1); -- rate limit is 0
|
||||
|
||||
-- Returns the main participants count and the visitors count
|
||||
@@ -136,6 +209,20 @@ local function cancel_destroy_timer(room)
|
||||
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
|
||||
local function schedule_destroy_timer(room)
|
||||
cancel_destroy_timer(room);
|
||||
@@ -165,7 +252,9 @@ module:hook('muc-occupant-left', function (event)
|
||||
if prosody.hosts[occupant_domain] and not is_admin(occupant.bare_jid) then
|
||||
local focus_occupant = get_focus_occupant(room);
|
||||
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;
|
||||
end
|
||||
-- Let's forward unavailable presence to the special jicofo
|
||||
@@ -194,10 +283,15 @@ module:hook('muc-occupant-left', function (event)
|
||||
if visitors_count == 0 then
|
||||
schedule_destroy_timer(room);
|
||||
end
|
||||
|
||||
if main_count == 0 then
|
||||
destroy_with_conference_ended(room);
|
||||
end
|
||||
end);
|
||||
|
||||
-- forward visitor presences to jicofo
|
||||
-- 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)
|
||||
local occupant = event.occupant;
|
||||
|
||||
@@ -227,10 +321,11 @@ module:hook('muc-broadcast-presence', function (event)
|
||||
full_p.attr.to = focus_occupant.jid;
|
||||
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
|
||||
if raiseHand then
|
||||
local user_id;
|
||||
local group_id;
|
||||
local is_moderator;
|
||||
local session = sessions[occupant.jid];
|
||||
local identity = session and session.jitsi_meet_context_user;
|
||||
@@ -246,14 +341,14 @@ module:hook('muc-broadcast-presence', function (event)
|
||||
-- so we can be auto promoted
|
||||
if identity and identity.id then
|
||||
user_id = session.jitsi_meet_context_user.id;
|
||||
group_id = session.jitsi_meet_context_group;
|
||||
|
||||
if room._data.moderator_id then
|
||||
if room._data.moderator_id == user_id then
|
||||
if session.auth_token and auto_promoted_with_token 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;
|
||||
end
|
||||
elseif session.auth_token then
|
||||
-- non-vpass and having a token is considered a moderator
|
||||
is_moderator = true;
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -272,6 +367,7 @@ module:hook('muc-broadcast-presence', function (event)
|
||||
jid = occupant.jid,
|
||||
time = raiseHand,
|
||||
userId = user_id,
|
||||
groupId = group_id,
|
||||
forcePromote = is_moderator and 'true' or 'false';
|
||||
}):up();
|
||||
|
||||
@@ -283,6 +379,18 @@ module:hook('muc-broadcast-presence', function (event)
|
||||
module:send(promotion_request);
|
||||
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;
|
||||
end);
|
||||
|
||||
@@ -317,7 +425,6 @@ local function stanza_handler(event)
|
||||
local room = get_room_from_jid(room_jid_match_rewrite(room_jid));
|
||||
|
||||
if not room then
|
||||
module:log('warn', 'No room found %s', room_jid);
|
||||
return;
|
||||
end
|
||||
|
||||
@@ -327,12 +434,7 @@ local function stanza_handler(event)
|
||||
end
|
||||
|
||||
-- respond with successful receiving the iq
|
||||
origin.send(st.iq({
|
||||
type = 'result';
|
||||
from = stanza.attr.to;
|
||||
to = stanza.attr.from;
|
||||
id = stanza.attr.id
|
||||
}));
|
||||
respond_iq_result(origin, stanza);
|
||||
|
||||
local req_jid = request_promotion.attr.jid;
|
||||
-- now let's find the occupant and forward the response
|
||||
@@ -469,7 +571,15 @@ module:hook('jicofo-unlock-room', function(e)
|
||||
return true;
|
||||
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 origin, stanza = event.origin, event.stanza;
|
||||
|
||||
@@ -500,7 +610,7 @@ local function iq_from_main_handler(event)
|
||||
local room = get_room_from_jid(room_jid_match_rewrite(room_jid));
|
||||
|
||||
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;
|
||||
end
|
||||
|
||||
@@ -523,27 +633,16 @@ local function iq_from_main_handler(event)
|
||||
end
|
||||
|
||||
-- respond with successful receiving the iq
|
||||
origin.send(st.iq({
|
||||
type = 'result';
|
||||
from = stanza.attr.to;
|
||||
to = stanza.attr.from;
|
||||
id = stanza.attr.id
|
||||
}));
|
||||
respond_iq_result(origin, stanza);
|
||||
|
||||
if process_disconnect then
|
||||
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;
|
||||
return destroy_with_conference_ended(room);
|
||||
end
|
||||
|
||||
-- if there is password supplied use it
|
||||
-- if this is update it will either set or remove the password
|
||||
room:set_password(node.attr.password);
|
||||
room._data.meetingId = node.attr.meetingId;
|
||||
room._data.moderator_id = node.attr.moderatorId;
|
||||
local createdTimestamp = node.attr.createdTimestamp;
|
||||
room.created_timestamp = createdTimestamp and tonumber(createdTimestamp) or nil;
|
||||
|
||||
@@ -553,6 +652,51 @@ local function iq_from_main_handler(event)
|
||||
room._main_room_lobby_enabled = false;
|
||||
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
|
||||
|
||||
local files = node:get_child('files');
|
||||
if files then
|
||||
local received_files = {};
|
||||
for _, child in ipairs(files.tags) do
|
||||
if child.name == 'file' then
|
||||
received_files[child.attr.id] = json.decode(child:get_text());
|
||||
end
|
||||
end
|
||||
|
||||
-- fire events so file sharing component will add/remove files and will notify clients
|
||||
local removed, added = table_compare(room.jitsi_shared_files or {}, received_files)
|
||||
for _, id in ipairs(removed) do
|
||||
module:context(local_domain):fire_event('jitsi-filesharing-remove', {
|
||||
room = room; id = id;
|
||||
});
|
||||
end
|
||||
for _, id in ipairs(added) do
|
||||
module:context(local_domain):fire_event('jitsi-filesharing-add', {
|
||||
room = room; file = received_files[id];
|
||||
});
|
||||
end
|
||||
end
|
||||
|
||||
if fire_jicofo_unlock then
|
||||
-- everything is connected allow participants to join
|
||||
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
|
||||
initiator.id = session.jitsi_meet_context_user.id;
|
||||
else
|
||||
initiator.id = session.granted_jitsi_meet_context_user_id;
|
||||
end
|
||||
if session.jitsi_meet_context_group ~= nil then
|
||||
initiator.group = session.jitsi_meet_context_group;
|
||||
end
|
||||
|
||||
initiator.group
|
||||
= session.jitsi_meet_context_group or session.granted_jitsi_meet_context_group_id;
|
||||
|
||||
app_data.file_recording_metadata.initiator = initiator
|
||||
update_app_data = true;
|
||||
|
196
roles/jitsi/files/prosody/modules/mod_jitsi_permissions.lua
Normal file
196
roles/jitsi/files/prosody/modules/mod_jitsi_permissions.lua
Normal file
@@ -0,0 +1,196 @@
|
||||
-- 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 module.');
|
||||
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.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;
|
||||
-- even if token and features are set we may want to re-send permissions
|
||||
occupant_session.force_permissions_update = true;
|
||||
elseif previous_affiliation == 'owner' and ( affiliation == 'member' or affiliation == 'none' ) then
|
||||
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;
|
||||
|
||||
if not session.jitsi_meet_context_features then
|
||||
session.jitsi_meet_context_features = default_permissions;
|
||||
end
|
||||
|
||||
room.send_default_permissions_to[bare_to] = nil;
|
||||
|
||||
stanza:tag('permissions', { xmlns='http://jitsi.org/jitmeet' });
|
||||
for k, v in pairs(session.jitsi_meet_context_features) do
|
||||
local val = tostring(v);
|
||||
stanza:tag('p', { name = k, val = val }):up();
|
||||
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();
|
||||
|
||||
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:
|
||||
-- * previd: for session resumption
|
||||
@@ -24,6 +25,8 @@ function init_session(event)
|
||||
session.jitsi_web_query_room = params.room;
|
||||
session.jitsi_web_query_prefix = params.prefix or "";
|
||||
end
|
||||
|
||||
session.user_region = request.headers[region_header_name];
|
||||
end
|
||||
|
||||
module:hook_global("bosh-session", init_session, 1);
|
||||
|
@@ -1,13 +1,15 @@
|
||||
--- activate under the main muc component
|
||||
local filters = require 'util.filters';
|
||||
local jid = require "util.jid";
|
||||
local jid_bare = require "util.jid".bare;
|
||||
local jid_host = require "util.jid".host;
|
||||
local st = require "util.stanza";
|
||||
local um_is_admin = require "core.usermanager".is_admin;
|
||||
local util = module:require "util";
|
||||
local is_admin = util.is_admin;
|
||||
local is_healthcheck_room = util.is_healthcheck_room;
|
||||
local is_moderated = util.is_moderated;
|
||||
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 MUC_NS = 'http://jabber.org/protocol/muc';
|
||||
|
||||
@@ -18,14 +20,18 @@ local function load_config()
|
||||
end
|
||||
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
|
||||
-- as moderators. As pre-join (where added) and joined event (where removed) happen one after another this list should
|
||||
-- have length of 1
|
||||
local joining_moderator_participants = {};
|
||||
local joining_moderator_participants = module:shared('moderators/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)
|
||||
local room, occupant = event.room, event.occupant;
|
||||
@@ -87,7 +93,7 @@ function filter_stanza(stanza)
|
||||
end
|
||||
|
||||
-- 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
|
||||
return stanza;
|
||||
end
|
||||
|
@@ -200,8 +200,14 @@ end
|
||||
|
||||
-- Managing breakout rooms
|
||||
|
||||
function create_breakout_room(room_jid, subject)
|
||||
local main_room, main_room_jid = get_main_room(room_jid);
|
||||
function create_breakout_room(orig_room, subject)
|
||||
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;
|
||||
|
||||
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);
|
||||
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);
|
||||
|
||||
if room_jid == main_room_jid then
|
||||
return;
|
||||
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);
|
||||
|
||||
if breakout_room then
|
||||
@@ -244,13 +255,18 @@ function destroy_breakout_room(room_jid, message)
|
||||
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);
|
||||
|
||||
if room_jid == main_room_jid then
|
||||
return;
|
||||
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._data.breakout_rooms then
|
||||
main_room._data.breakout_rooms[room_jid] = name;
|
||||
@@ -322,18 +338,25 @@ function on_message(event)
|
||||
end
|
||||
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
elseif message.attr.type == JSON_TYPE_MOVE_TO_ROOM_REQUEST then
|
||||
local participant_jid = message.attr.participantJid;
|
||||
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({
|
||||
type = BREAKOUT_ROOMS_IDENTITY_TYPE,
|
||||
event = JSON_TYPE_MOVE_TO_ROOM_REQUEST,
|
||||
@@ -342,6 +365,7 @@ function on_message(event)
|
||||
|
||||
if not json_msg then
|
||||
module:log('error', 'skip sending request room:%s error:%s', room.jid, error);
|
||||
return false
|
||||
end
|
||||
|
||||
send_json_msg(participant_jid, json_msg)
|
||||
@@ -416,6 +440,16 @@ function exist_occupants_in_rooms(main_room)
|
||||
return false;
|
||||
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)
|
||||
local room_jid = event.room.jid;
|
||||
|
||||
@@ -481,7 +515,7 @@ function on_main_room_destroyed(event)
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
@@ -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-left', on_occupant_left);
|
||||
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)
|
||||
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';
|
||||
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 (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, {
|
||||
name = 'muc#roominfo_breakout_main_room';
|
||||
label = 'The main room associated with this breakout room';
|
||||
value = main_room_jid;
|
||||
value = internal_room_jid_match_rewrite(main_room_jid);
|
||||
});
|
||||
end);
|
||||
|
||||
|
56
roles/jitsi/files/prosody/modules/mod_muc_displayname.lua
Normal file
56
roles/jitsi/files/prosody/modules/mod_muc_displayname.lua
Normal file
@@ -0,0 +1,56 @@
|
||||
--- This module removes identity information from presence stanzas when the
|
||||
--- hideDisplayNameForAll or hideDisplayNameForGuests options are enabled
|
||||
--- for a room.
|
||||
|
||||
--- To be enabled under the main muc component
|
||||
local filters = require 'util.filters';
|
||||
local st = require 'util.stanza';
|
||||
|
||||
local util = module:require 'util';
|
||||
local filter_identity_from_presence = util.filter_identity_from_presence;
|
||||
local get_room_by_name_and_subdomain = util.get_room_by_name_and_subdomain;
|
||||
local is_admin = util.is_admin;
|
||||
local ends_with = util.ends_with;
|
||||
local internal_room_jid_match_rewrite = util.internal_room_jid_match_rewrite;
|
||||
|
||||
-- we need to get the shared resource for joining moderators, as participants are marked as moderators
|
||||
-- after joining which is after the filter for stanza/out, but we need to know will this participant be a moderator
|
||||
local joining_moderator_participants = module:shared('moderators/joining_moderator_participants');
|
||||
|
||||
--- Filter presence sent to non-moderator members of a room when the hideDisplayNameForGuests option is set.
|
||||
function filter_stanza_out(stanza, session)
|
||||
if stanza.name ~= 'presence' or stanza.attr.type == 'error'
|
||||
or stanza.attr.type == 'unavailable' or ends_with(stanza.attr.from, '/focus') then
|
||||
return stanza;
|
||||
end
|
||||
|
||||
local room = get_room_by_name_and_subdomain(session.jitsi_web_query_room, session.jitsi_web_query_prefix);
|
||||
local shouldFilter = false;
|
||||
|
||||
if room and (room._data.hideDisplayNameForGuests == true or room._data.hideDisplayNameForAll == true) then
|
||||
local occupant = room:get_occupant_by_real_jid(stanza.attr.to);
|
||||
-- don't touch self-presence
|
||||
if occupant and stanza.attr.from ~= internal_room_jid_match_rewrite(occupant.nick) then
|
||||
local isModerator = (occupant.role == 'moderator' or joining_moderator_participants[occupant.bare_jid]);
|
||||
shouldFilter = room._data.hideDisplayNameForAll or not isModerator;
|
||||
end
|
||||
end
|
||||
|
||||
if shouldFilter then
|
||||
return filter_identity_from_presence(stanza);
|
||||
else
|
||||
return stanza;
|
||||
end
|
||||
end
|
||||
|
||||
function filter_session(session)
|
||||
filters.add_filter(session, 'stanzas/out', filter_stanza_out, -100);
|
||||
end
|
||||
|
||||
function module.load()
|
||||
filters.add_filter_hook(filter_session);
|
||||
end
|
||||
|
||||
function module.unload()
|
||||
filters.remove_filter_hook(filter_session);
|
||||
end
|
@@ -4,9 +4,9 @@
|
||||
-- Copyright (C) 2023-present 8x8, Inc.
|
||||
|
||||
local oss_util = module:require "util";
|
||||
local is_admin = oss_util.is_admin;
|
||||
local is_healthcheck_room = oss_util.is_healthcheck_room;
|
||||
local process_host_module = oss_util.process_host_module;
|
||||
local um_is_admin = require "core.usermanager".is_admin;
|
||||
local inspect = require('inspect');
|
||||
local jid_bare = require "util.jid".bare;
|
||||
local jid = require "util.jid";
|
||||
@@ -21,10 +21,6 @@ if lobby_muc_component_config == nil then
|
||||
return ;
|
||||
end
|
||||
|
||||
local function is_admin(occupant_jid)
|
||||
return um_is_admin(occupant_jid, module.host);
|
||||
end
|
||||
|
||||
local function remove_flip_tag(stanza)
|
||||
stanza:maptags(function(tag)
|
||||
if tag and tag.name == "flip_device" then
|
||||
|
@@ -1,6 +1,30 @@
|
||||
-- This module makes all MUCs in Prosody unavailable on disco#items query
|
||||
-- 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);
|
||||
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
|
||||
|
||||
if not from_real_jid then
|
||||
return nil;
|
||||
end
|
||||
|
||||
local is_from_moderator = lobby_room:get_affiliation(from_real_jid) == 'owner';
|
||||
|
||||
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 uuid_gen = require "util.uuid".generate;
|
||||
local main_util = module:require "util";
|
||||
local is_admin = main_util.is_admin;
|
||||
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 internal_room_jid_match_rewrite = main_util.internal_room_jid_match_rewrite;
|
||||
local presence_check_status = main_util.presence_check_status;
|
||||
|
||||
local um_is_admin = require 'core.usermanager'.is_admin;
|
||||
local function is_admin(jid)
|
||||
return um_is_admin(jid, module.host);
|
||||
end
|
||||
local extract_subdomain = main_util.extract_subdomain;
|
||||
|
||||
local QUEUE_MAX_SIZE = 500;
|
||||
|
||||
-- Module that 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)
|
||||
module:depends("jitsi_permissions");
|
||||
|
||||
-- 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
|
||||
module:hook("muc-room-created", function(event)
|
||||
@@ -75,17 +83,35 @@ module:hook('muc-broadcast-presence', function (event)
|
||||
end
|
||||
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 room, stanza = event.room, event.stanza;
|
||||
|
||||
-- we skip processing only if jicofo_lock is set to false
|
||||
if room._data.jicofo_lock == false or is_healthcheck_room(room.jid) then
|
||||
local function process_region(session, stanza)
|
||||
if not session.user_region then
|
||||
return;
|
||||
end
|
||||
|
||||
local region = stanza:get_child_text('jitsi_participant_region');
|
||||
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;
|
||||
end
|
||||
|
||||
local occupant = event.occupant;
|
||||
if ends_with(occupant.nick, '/focus') then
|
||||
module:fire_event('jicofo-unlock-room', { room = room; });
|
||||
else
|
||||
@@ -131,3 +157,87 @@ module:hook('jicofo-unlock-room', handle_jicofo_unlock);
|
||||
module:hook("muc-occupant-groupchat", function(event)
|
||||
event.stanza:remove_children('nick', 'http://jabber.org/protocol/nick');
|
||||
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')
|
||||
or stanza:get_child_text('json-message');
|
||||
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 module has the option to set participants to moderators when connected via token/when they are authenticated
|
||||
-- This module depends on mod_persistent_lobby.
|
||||
local um_is_admin = require 'core.usermanager'.is_admin;
|
||||
local jid = require 'util.jid';
|
||||
local util = module:require "util";
|
||||
local is_admin = util.is_admin;
|
||||
local is_healthcheck_room = util.is_healthcheck_room;
|
||||
local is_moderated = util.is_moderated;
|
||||
local process_host_module = util.process_host_module;
|
||||
@@ -17,7 +17,7 @@ local disable_auto_owners = module:get_option_boolean('wait_for_host_disable_aut
|
||||
|
||||
local muc_domain_base = module:get_option_string('muc_mapper_domain_base');
|
||||
if not muc_domain_base then
|
||||
module:log('warn', "No 'muc_mapper_domain_base' option set, disabling muc_mapper plugin inactive");
|
||||
module:log('warn', "No 'muc_mapper_domain_base' option set, disabling module");
|
||||
return
|
||||
end
|
||||
|
||||
@@ -43,10 +43,6 @@ if not disable_auto_owners then
|
||||
end, 2);
|
||||
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
|
||||
-- and wait for the moderator to join
|
||||
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 is_healthcheck_room = util.is_healthcheck_room;
|
||||
|
||||
-- Checks if the given stanza contains a JSON message,
|
||||
-- and that the message type pertains to the polls feature.
|
||||
-- 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
|
||||
local POLLS_LIMIT = 128;
|
||||
local POLL_PAYLOAD_LIMIT = 1024;
|
||||
|
||||
-- Logs a warning and returns true if a room does not
|
||||
-- have poll data associated with it.
|
||||
@@ -72,6 +54,7 @@ module:hook("muc-room-created", function(event)
|
||||
room.polls = {
|
||||
by_id = {};
|
||||
order = {};
|
||||
count = 0;
|
||||
};
|
||||
end);
|
||||
|
||||
@@ -79,27 +62,46 @@ end);
|
||||
-- by listening to "new-poll" and "answer-poll" messages,
|
||||
-- and updating the room poll data accordingly.
|
||||
-- This mirrors the client-side poll update logic.
|
||||
module:hook("message/bare", function(event)
|
||||
local data = get_poll_message(event.stanza);
|
||||
if data == nil then return end
|
||||
module:hook('jitsi-endpoint-message-received', function(event)
|
||||
local data, error, occupant, room, origin, stanza
|
||||
= 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 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)
|
||||
if not poll_creator then
|
||||
module:log("error", "Cannot retrieve poll creator id and name for %s from %s", occupant.jid, room.jid)
|
||||
return
|
||||
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 compact_answers = {}
|
||||
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
|
||||
table.insert(room.polls.order, poll)
|
||||
room.polls.count = room.polls.count + 1;
|
||||
|
||||
local pollData = {
|
||||
event = event,
|
||||
@@ -130,16 +133,9 @@ module:hook("message/bare", function(event)
|
||||
}
|
||||
}
|
||||
module:fire_event("poll-created", pollData);
|
||||
|
||||
elseif data.type == "answer-poll" then
|
||||
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];
|
||||
if poll == nil then
|
||||
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
|
||||
-- for that session.
|
||||
function on_message(event)
|
||||
if event and event["stanza"] then
|
||||
if event.origin and event.origin.jitsi_meet_context_user then
|
||||
|
||||
local stanza, session = event.stanza, event.origin;
|
||||
if stanza and session then
|
||||
update_presence_identity(
|
||||
event.stanza,
|
||||
event.origin.jitsi_meet_context_user,
|
||||
event.origin.jitsi_meet_context_group
|
||||
stanza,
|
||||
session.jitsi_meet_context_user,
|
||||
session.jitsi_meet_context_group
|
||||
);
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@@ -14,6 +14,7 @@ local ip_util = require "util.ip";
|
||||
local new_ip = ip_util.new_ip;
|
||||
local match_ip = ip_util.match;
|
||||
local parse_cidr = ip_util.parse_cidr;
|
||||
local get_ip = module:require "util".get_ip;
|
||||
|
||||
local config = {};
|
||||
local limits_resolution = 1;
|
||||
@@ -76,14 +77,6 @@ local function is_whitelisted_host(h)
|
||||
return config.whitelist_hosts:contains(h);
|
||||
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
|
||||
local function limit_ip(ip)
|
||||
module:log("info", "Limiting %s due to login/join rate exceeded.", ip);
|
||||
@@ -192,9 +185,8 @@ local function filter_hook(session)
|
||||
return;
|
||||
end
|
||||
|
||||
local request = get_request_from_conn(session.conn);
|
||||
local ip = request and request.ip or session.ip;
|
||||
module:log("debug", "New session from %s", ip);
|
||||
local ip = get_ip(session);
|
||||
module:log("debug", "New session from %s", ip);
|
||||
if is_whitelisted(ip) or is_whitelisted_host(session.host) then
|
||||
return;
|
||||
end
|
||||
|
@@ -1,10 +1,6 @@
|
||||
-- Generic room metadata
|
||||
-- See mod_room_metadata_component.lua
|
||||
|
||||
local COMPONENT_IDENTITY_TYPE = 'room_metadata';
|
||||
local room_metadata_component_host = module:get_option_string('room_metadata_component', 'metadata.'..module.host);
|
||||
-- TODO: Remove this file after several stable releases when people update their configs
|
||||
module:log('warn', 'mod_room_metadata is deprecated and will be removed in a future release. '
|
||||
.. 'Please update your config by removing this module from the list of loaded modules.');
|
||||
|
||||
module:depends("jitsi_session");
|
||||
|
||||
-- Advertise the component so clients can pick up the address and use it
|
||||
module:add_identity('component', COMPONENT_IDENTITY_TYPE, room_metadata_component_host);
|
||||
module:depends("features_identity");
|
||||
|
@@ -1,34 +1,40 @@
|
||||
-- This module implements a generic metadata storage system for rooms.
|
||||
--
|
||||
-- VirtualHost "jitmeet.example.com"
|
||||
-- modules_enabled = {
|
||||
-- "room_metadata"
|
||||
-- }
|
||||
-- room_metadata_component = "metadata.jitmeet.example.com"
|
||||
-- main_muc = "conference.jitmeet.example.com"
|
||||
--
|
||||
-- Component "metadata.jitmeet.example.com" "room_metadata_component"
|
||||
-- muc_component = "conference.jitmeet.example.com"
|
||||
-- breakout_rooms_component = "breakout.jitmeet.example.com"
|
||||
|
||||
local array = require 'util.array';
|
||||
local filters = require 'util.filters';
|
||||
local jid_node = require 'util.jid'.node;
|
||||
local json = require 'cjson.safe';
|
||||
local json = require 'util.json';
|
||||
local st = require 'util.stanza';
|
||||
local jid = require 'util.jid';
|
||||
|
||||
local util = module:require 'util';
|
||||
local is_admin = util.is_admin;
|
||||
local is_healthcheck_room = util.is_healthcheck_room;
|
||||
local get_room_from_jid = util.get_room_from_jid;
|
||||
local room_jid_match_rewrite = util.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 table_shallow_copy = util.table_shallow_copy;
|
||||
local table_add = util.table_add;
|
||||
local table_equals = util.table_equals;
|
||||
|
||||
local MUC_NS = 'http://jabber.org/protocol/muc';
|
||||
local COMPONENT_IDENTITY_TYPE = 'room_metadata';
|
||||
local FORM_KEY = 'muc#roominfo_jitsimetadata';
|
||||
|
||||
local muc_component_host = module:get_option_string('muc_component');
|
||||
|
||||
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 main_virtual_host = module:get_option_string('muc_mapper_domain_base');
|
||||
if not main_virtual_host then
|
||||
module:log('warn', 'No muc_mapper_domain_base option set.');
|
||||
return;
|
||||
end
|
||||
|
||||
@@ -40,10 +46,13 @@ local main_muc_module;
|
||||
|
||||
-- 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({
|
||||
type = COMPONENT_IDENTITY_TYPE,
|
||||
metadata = room.jitsiMetadata or {}
|
||||
metadata = metadata or room.jitsiMetadata or {}
|
||||
});
|
||||
|
||||
if not res then
|
||||
@@ -53,28 +62,53 @@ function getMetadataJSON(room)
|
||||
return res;
|
||||
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)
|
||||
local json_msg = getMetadataJSON(room);
|
||||
|
||||
if not json_msg then
|
||||
return;
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
function send_json_msg(to_jid, room_jid, json_msg)
|
||||
local stanza = st.message({ from = module.host; to = to_jid; })
|
||||
:tag('json-message', { xmlns = 'http://jitsi.org/jitmeet', room = room_jid }):text(json_msg):up();
|
||||
function send_metadata(occupant, room, json_msg)
|
||||
if not json_msg or is_admin(occupant.bare_jid) then
|
||||
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;
|
||||
local moderators = array();
|
||||
|
||||
if room._data.participants then
|
||||
participants = array();
|
||||
participants:append(room._data.participants);
|
||||
end
|
||||
|
||||
if room._data.moderator_id then
|
||||
moderators:push(room._data.moderator_id);
|
||||
end
|
||||
|
||||
if room._data.moderators then
|
||||
moderators:append(room._data.moderators);
|
||||
end
|
||||
|
||||
metadata_to_send = table_shallow_copy(metadata_to_send);
|
||||
metadata_to_send.participants = participants;
|
||||
metadata_to_send.moderators = moderators;
|
||||
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);
|
||||
end
|
||||
|
||||
@@ -84,12 +118,14 @@ function room_created(event)
|
||||
local room = event.room;
|
||||
|
||||
if is_healthcheck_room(room.jid) then
|
||||
return ;
|
||||
return;
|
||||
end
|
||||
|
||||
if not room.jitsiMetadata then
|
||||
room.jitsiMetadata = {};
|
||||
end
|
||||
|
||||
room.sent_initial_metadata = {};
|
||||
end
|
||||
|
||||
function on_message(event)
|
||||
@@ -129,11 +165,6 @@ function on_message(event)
|
||||
return false;
|
||||
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);
|
||||
if jsonData == nil then -- invalid JSON
|
||||
module:log("error", "Invalid JSON message: %s error:%s", messageText, error);
|
||||
@@ -145,12 +176,28 @@ function on_message(event)
|
||||
return false;
|
||||
end
|
||||
|
||||
room.jitsiMetadata[jsonData.key] = jsonData.data;
|
||||
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(main_virtual_host):fire_event('jitsi-metadata-allow-moderation',
|
||||
{ room = room; actor = occupant; key = jsonData.key ; data = jsonData.data; session = session; });
|
||||
|
||||
broadcastMetadata(room);
|
||||
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
|
||||
|
||||
-- fire and event for the change
|
||||
main_muc_module:fire_event('jitsi-metadata-updated', { room = room; actor = occupant; key = jsonData.key; });
|
||||
jsonData.data = res;
|
||||
end
|
||||
|
||||
local old_value = room.jitsiMetadata[jsonData.key];
|
||||
if not table_equals(old_value, jsonData.data) then
|
||||
room.jitsiMetadata[jsonData.key] = jsonData.data;
|
||||
|
||||
broadcastMetadata(room);
|
||||
|
||||
-- fire and event for the change
|
||||
main_muc_module:fire_event('jitsi-metadata-updated', { room = room; actor = occupant; key = jsonData.key; });
|
||||
end
|
||||
|
||||
return true;
|
||||
end
|
||||
@@ -169,21 +216,51 @@ function process_main_muc_loaded(main_muc, host_module)
|
||||
|
||||
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).
|
||||
host_module:hook("room-metadata-changed", function(event)
|
||||
broadcastMetadata(event.room);
|
||||
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 {};
|
||||
|
||||
local audioNewValue = startMuted.attr.audio == 'true';
|
||||
local videoNewValue = startMuted.attr.video == 'true';
|
||||
local send_update = false;
|
||||
|
||||
if startMutedMetadata.audio ~= audioNewValue then
|
||||
startMutedMetadata.audio = audioNewValue;
|
||||
send_update = true;
|
||||
end
|
||||
if startMutedMetadata.video ~= videoNewValue then
|
||||
startMutedMetadata.video = videoNewValue;
|
||||
send_update = true;
|
||||
end
|
||||
|
||||
if send_update then
|
||||
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
|
||||
@@ -208,18 +285,6 @@ function process_breakout_muc_loaded(breakout_muc, host_module)
|
||||
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-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
|
||||
|
||||
if breakout_rooms_component_host then
|
||||
@@ -238,3 +303,59 @@ if breakout_rooms_component_host then
|
||||
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);
|
||||
|
||||
process_host_module(main_virtual_host, function(host_module)
|
||||
module:context(host_module.host):fire_event('jitsi-add-identity', {
|
||||
name = 'room_metadata'; host = module.host;
|
||||
});
|
||||
end);
|
||||
|
138
roles/jitsi/files/prosody/modules/mod_short_lived_token.lua
Normal file
138
roles/jitsi/files/prosody/modules/mod_short_lived_token.lua
Normal file
@@ -0,0 +1,138 @@
|
||||
-- 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_id,
|
||||
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
|
||||
},
|
||||
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_id,
|
||||
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;
|
||||
transport = 'https';
|
||||
port = 443;
|
||||
});
|
||||
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,6 +1,6 @@
|
||||
local speakerstats_component
|
||||
= module:get_option_string("speakerstats_component", "speakerstats."..module.host);
|
||||
-- TODO: Remove this file after several stable releases when people update their configs
|
||||
module:log('warn', 'mod_speakerstats is deprecated and will be removed in a future release. '
|
||||
.. 'Please update your config by removing this module from the list of loaded modules.');
|
||||
|
||||
-- Advertise speaker stats so client can pick up the address and start sending
|
||||
-- dominant speaker events
|
||||
module:add_identity("component", "speakerstats", speakerstats_component);
|
||||
module:depends('jitsi_session');
|
||||
module:depends('features_identity');
|
||||
|
@@ -1,13 +1,15 @@
|
||||
local util = module:require "util";
|
||||
local is_admin = util.is_admin;
|
||||
local get_room_from_jid = util.get_room_from_jid;
|
||||
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 process_host_module = util.process_host_module;
|
||||
local is_transcriber_jigasi = util.is_transcriber_jigasi;
|
||||
local jid_resource = require "util.jid".resource;
|
||||
local st = require "util.stanza";
|
||||
local socket = require "socket";
|
||||
local json = require 'cjson.safe';
|
||||
local um_is_admin = require "core.usermanager".is_admin;
|
||||
local jid_split = require 'util.jid'.split;
|
||||
|
||||
-- we use async to detect Prosody 0.10 and earlier
|
||||
@@ -18,22 +20,18 @@ if not have_async then
|
||||
end
|
||||
|
||||
local muc_component_host = module:get_option_string("muc_component");
|
||||
local muc_domain_base = module:get_option_string("muc_mapper_domain_base");
|
||||
local main_virtual_host = module:get_option_string("muc_mapper_domain_base");
|
||||
|
||||
if muc_component_host == nil or muc_domain_base == nil then
|
||||
if muc_component_host == nil or main_virtual_host == nil then
|
||||
module:log("error", "No muc_component specified. No muc to operate on!");
|
||||
return;
|
||||
end
|
||||
local breakout_room_component_host = "breakout." .. muc_domain_base;
|
||||
local breakout_room_component_host = "breakout." .. main_virtual_host;
|
||||
|
||||
module:log("info", "Starting speakerstats for %s", muc_component_host);
|
||||
|
||||
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
|
||||
-- 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()
|
||||
@@ -221,14 +219,15 @@ end
|
||||
|
||||
-- Create SpeakerStats object for the joined user
|
||||
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;
|
||||
end
|
||||
|
||||
local occupant = event.occupant;
|
||||
|
||||
local nick = jid_resource(occupant.nick);
|
||||
|
||||
if room.speakerStats then
|
||||
@@ -377,3 +376,9 @@ process_host_module(breakout_room_component_host, function(host_module, host)
|
||||
end);
|
||||
end
|
||||
end);
|
||||
|
||||
process_host_module(main_virtual_host, function(host_module)
|
||||
module:context(host_module.host):fire_event('jitsi-add-identity', {
|
||||
name = 'speakerstats'; host = module.host;
|
||||
});
|
||||
end);
|
||||
|
@@ -15,7 +15,6 @@ local get_room_from_jid = util.get_room_from_jid;
|
||||
local st = require "util.stanza";
|
||||
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", "");
|
||||
|
||||
if asapKeyServer then
|
||||
|
@@ -4,19 +4,17 @@
|
||||
local log = module._log;
|
||||
local host = module.host;
|
||||
local st = require "util.stanza";
|
||||
local um_is_admin = require "core.usermanager".is_admin;
|
||||
local jid_split = require 'util.jid'.split;
|
||||
local jid_bare = require 'util.jid'.bare;
|
||||
|
||||
local util = module:require 'util';
|
||||
local is_admin = util.is_admin;
|
||||
|
||||
local DEBUG = false;
|
||||
|
||||
local measure_success = module:measure('success', '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.+)")();
|
||||
if parentHostName == nil then
|
||||
module:log("error", "Failed to start - unable to get parent hostname");
|
||||
|
@@ -12,13 +12,15 @@ local st = require 'util.stanza';
|
||||
local jid = require 'util.jid';
|
||||
local new_id = require 'util.id'.medium;
|
||||
local util = module:require 'util';
|
||||
local filter_identity_from_presence = util.filter_identity_from_presence;
|
||||
local is_admin = util.is_admin;
|
||||
local presence_check_status = util.presence_check_status;
|
||||
local process_host_module = util.process_host_module;
|
||||
local is_transcriber_jigasi = util.is_transcriber_jigasi;
|
||||
local json = require 'cjson.safe';
|
||||
|
||||
local um_is_admin = require 'core.usermanager'.is_admin;
|
||||
local function is_admin(jid)
|
||||
return um_is_admin(jid, module.host);
|
||||
end
|
||||
-- Debug flag
|
||||
local DEBUG = false;
|
||||
|
||||
local MUC_NS = 'http://jabber.org/protocol/muc';
|
||||
|
||||
@@ -57,7 +59,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
|
||||
local iq_id = new_id();
|
||||
sent_iq_cache:set(iq_id, socket.gettime());
|
||||
local connect_done = st.iq({
|
||||
local visitors_iq = st.iq({
|
||||
type = 'set',
|
||||
to = conference_service,
|
||||
from = module.host,
|
||||
@@ -68,11 +70,54 @@ local function send_visitors_iq(conference_service, room, type)
|
||||
password = type ~= 'disconnect' and room:get_password() or '',
|
||||
lobby = room._data.lobbyroom and 'true' or 'false',
|
||||
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
|
||||
}):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();
|
||||
|
||||
-- files that are shared in the room
|
||||
if room.jitsi_shared_files then
|
||||
visitors_iq:tag('files', { xmlns = 'jitsi:visitors' });
|
||||
for k, v in pairs(room.jitsi_shared_files) do
|
||||
visitors_iq:tag('file', {
|
||||
id = k
|
||||
}):text(json.encode(v)):up();
|
||||
end
|
||||
visitors_iq:up();
|
||||
end
|
||||
end
|
||||
|
||||
visitors_iq:up();
|
||||
|
||||
module:send(visitors_iq);
|
||||
end
|
||||
|
||||
-- Filter out identity information (nick name, email, etc) from a presence stanza,
|
||||
-- if the hideDisplayNameForGuests option for the room is set (note that the
|
||||
-- hideDisplayNameForAll option is implemented in a diffrent way and does not
|
||||
-- require filtering here)
|
||||
-- This is applied to presence of main room participants before it is sent out to
|
||||
-- vnodes.
|
||||
local function filter_stanza_nick_if_needed(stanza, room)
|
||||
if not stanza or stanza.name ~= 'presence' or stanza.attr.type == 'error' or stanza.attr.type == 'unavailable' then
|
||||
return stanza;
|
||||
end
|
||||
|
||||
-- if hideDisplayNameForGuests we want to drop any display name from the presence stanza
|
||||
if room and (room._data.hideDisplayNameForGuests or room._data.hideDisplayNameForAll) then
|
||||
return filter_identity_from_presence(stanza);
|
||||
end
|
||||
|
||||
return stanza;
|
||||
end
|
||||
|
||||
-- an event received from visitors component, which receives iqs from jicofo
|
||||
@@ -96,9 +141,12 @@ local function connect_vnode(event)
|
||||
|
||||
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
|
||||
if not is_admin(o.bare_jid) then
|
||||
local fmuc_pr = st.clone(o:get_presence());
|
||||
local fmuc_pr = filter_stanza_nick_if_needed(st.clone(o:get_presence()), room);
|
||||
local user, _, res = jid.split(o.nick);
|
||||
fmuc_pr.attr.to = jid.join(user, conference_service , res);
|
||||
fmuc_pr.attr.from = o.jid;
|
||||
@@ -181,11 +229,12 @@ end, 900);
|
||||
process_host_module(main_muc_component_config, function(host_module, host)
|
||||
-- detects presence change in a main participant and propagate it to the used visitor nodes
|
||||
host_module:hook('muc-occupant-pre-change', function (event)
|
||||
local room, stanza, occupant = event.room, event.stanza, event.dest_occupant;
|
||||
local room, stanzaEv, occupant = event.room, event.stanza, event.dest_occupant;
|
||||
local stanza = filter_stanza_nick_if_needed(stanzaEv, room);
|
||||
|
||||
-- filter focus and configured domains (used for jibri and transcribers)
|
||||
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;
|
||||
end
|
||||
|
||||
@@ -202,11 +251,12 @@ process_host_module(main_muc_component_config, function(host_module, host)
|
||||
|
||||
-- when a main participant leaves inform the visitor nodes
|
||||
host_module:hook('muc-occupant-left', function (event)
|
||||
local room, stanza, occupant = event.room, event.stanza, event.occupant;
|
||||
local room, stanzaEv, occupant = event.room, event.stanza, event.occupant;
|
||||
local stanza = filter_stanza_nick_if_needed(stanzaEv, room);
|
||||
|
||||
-- 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
|
||||
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;
|
||||
end
|
||||
|
||||
@@ -245,11 +295,12 @@ process_host_module(main_muc_component_config, function(host_module, host)
|
||||
|
||||
-- detects new participants joining main room and sending them to the visitor nodes
|
||||
host_module:hook('muc-occupant-joined', function (event)
|
||||
local room, stanza, occupant = event.room, event.stanza, event.occupant;
|
||||
local room, stanzaEv, occupant = event.room, event.stanza, event.occupant;
|
||||
local stanza = filter_stanza_nick_if_needed(stanzaEv, room);
|
||||
|
||||
-- filter focus, ignore configured domains (jibri and transcribers)
|
||||
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;
|
||||
end
|
||||
|
||||
@@ -257,6 +308,10 @@ process_host_module(main_muc_component_config, function(host_module, host)
|
||||
local user, _, res = jid.split(occupant.nick);
|
||||
-- a main participant we need to update all active visitor nodes
|
||||
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);
|
||||
fmuc_pr.attr.to = jid.join(user, k, res);
|
||||
fmuc_pr.attr.from = occupant.jid;
|
||||
@@ -265,10 +320,11 @@ process_host_module(main_muc_component_config, function(host_module, host)
|
||||
end);
|
||||
-- forwards messages from main participants to vnodes
|
||||
host_module:hook('muc-occupant-groupchat', function(event)
|
||||
local room, stanza, occupant = event.room, event.stanza, event.occupant;
|
||||
local room, stanzaEv, occupant = event.room, event.stanza, event.occupant;
|
||||
local stanza = filter_stanza_nick_if_needed(stanzaEv, room);
|
||||
|
||||
-- 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;
|
||||
end
|
||||
|
||||
@@ -285,7 +341,8 @@ process_host_module(main_muc_component_config, function(host_module, host)
|
||||
-- receiving messages from visitor nodes and forward them to local main participants
|
||||
-- and forward them to the rest of visitor nodes
|
||||
host_module:hook('muc-occupant-groupchat', function(event)
|
||||
local occupant, room, stanza = event.occupant, event.room, event.stanza;
|
||||
local occupant, room, stanzaEv = event.occupant, event.room, event.stanza;
|
||||
local stanza = filter_stanza_nick_if_needed(stanzaEv, room);
|
||||
local to = stanza.attr.to;
|
||||
local from = stanza.attr.from;
|
||||
local from_vnode = jid.host(from);
|
||||
@@ -296,6 +353,11 @@ process_host_module(main_muc_component_config, function(host_module, host)
|
||||
return;
|
||||
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
|
||||
stanza.attr.from = to;
|
||||
for _, o in room:each_occupant() do
|
||||
@@ -331,25 +393,34 @@ process_host_module(main_muc_component_config, function(host_module, host)
|
||||
end
|
||||
end
|
||||
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);
|
||||
|
||||
module:hook('jitsi-lobby-enabled', function(event)
|
||||
local function update_vnodes_for_room(event)
|
||||
local room = event.room;
|
||||
if visitors_nodes[room.jid] then
|
||||
-- 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');
|
||||
if visitors_nodes[room.jid] then
|
||||
-- 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
|
||||
end);
|
||||
module:hook('jitsi-lobby-disabled', function(event)
|
||||
local room = event.room;
|
||||
if visitors_nodes[room.jid] then
|
||||
-- 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);
|
||||
end
|
||||
|
||||
module:hook('jitsi-lobby-enabled', update_vnodes_for_room);
|
||||
module:hook('jitsi-lobby-disabled', update_vnodes_for_room);
|
||||
module:hook('jitsi-filesharing-updated', update_vnodes_for_room);
|
||||
|
@@ -1,9 +1,11 @@
|
||||
module:log('info', 'Starting visitors_component at %s', module.host);
|
||||
|
||||
local array = require "util.array";
|
||||
local http = require 'net.http';
|
||||
local jid = require 'util.jid';
|
||||
local st = require 'util.stanza';
|
||||
local util = module:require 'util';
|
||||
local is_admin = util.is_admin;
|
||||
local is_healthcheck_room = util.is_healthcheck_room;
|
||||
local is_sip_jigasi = util.is_sip_jigasi;
|
||||
local room_jid_match_rewrite = util.room_jid_match_rewrite;
|
||||
@@ -11,14 +13,19 @@ local get_room_from_jid = util.get_room_from_jid;
|
||||
local get_focus_occupant = util.get_focus_occupant;
|
||||
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 table_find = util.table_find;
|
||||
local is_vpaas = util.is_vpaas;
|
||||
local is_sip_jibri_join = util.is_sip_jibri_join;
|
||||
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 um_is_admin = require 'core.usermanager'.is_admin;
|
||||
local json = require 'cjson.safe';
|
||||
local inspect = require 'inspect';
|
||||
|
||||
-- Debug flag
|
||||
local DEBUG = false;
|
||||
|
||||
-- will be initialized once the main virtual host module is initialized
|
||||
local token_util;
|
||||
|
||||
@@ -47,10 +54,6 @@ local http_headers = {
|
||||
["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
|
||||
-- automatically allowed or allowed by a moderator
|
||||
local visitors_promotion_map = {};
|
||||
@@ -63,15 +66,15 @@ local visitors_promotion_requests = {};
|
||||
local cache = require 'util.cache';
|
||||
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
|
||||
}));
|
||||
-- Function to get visitors room metadata
|
||||
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
|
||||
|
||||
-- Sends a json-message to the destination jid
|
||||
@@ -83,13 +86,38 @@ function send_json_message(to_jid, json_message)
|
||||
module:send(stanza);
|
||||
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 DEBUG then
|
||||
module:log('debug', 'Received promotion request from %s for room %s, nick: %s, time: %s, user_id: %s, group_id: %s, force_promote_requested: %s',
|
||||
from_jid, room.jid, nick, time, user_id, group_id, force_promote_requested);
|
||||
end
|
||||
|
||||
-- if visitors is enabled for the room
|
||||
if visitors_promotion_map[room.jid] then
|
||||
local force_promote = auto_allow_promotion or get_visitors_room_metadata(room).autoPromote;
|
||||
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
|
||||
if time and time > 0 and (
|
||||
auto_allow_promotion
|
||||
or force_promote == 'true') then
|
||||
if time and time > 0 and force_promote then
|
||||
-- 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
|
||||
local username = new_id():lower();
|
||||
@@ -179,7 +207,7 @@ local function connect_vnode_received(room, vnode)
|
||||
room._connected_vnodes = cache.new(16); -- we up to 16 vnodes for this prosody
|
||||
end
|
||||
|
||||
room._connected_vnodes:set(vnode..'.meet.jitsi', 'connected');
|
||||
room._connected_vnodes:set(vnode..'.meet.jitsi', {});
|
||||
end
|
||||
|
||||
local function disconnect_vnode_received(room, vnode)
|
||||
@@ -194,6 +222,34 @@ local function disconnect_vnode_received(room, vnode)
|
||||
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
|
||||
|
||||
-- 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
|
||||
local function stanza_handler(event)
|
||||
@@ -203,6 +259,10 @@ local function stanza_handler(event)
|
||||
return;
|
||||
end
|
||||
|
||||
if DEBUG then
|
||||
module:log('debug', 'Received stanza %s from %s', stanza, origin.full_jid);
|
||||
end
|
||||
|
||||
if stanza.attr.type == 'result' and sent_iq_cache:get(stanza.attr.id) then
|
||||
sent_iq_cache:set(stanza.attr.id, nil);
|
||||
return true;
|
||||
@@ -229,15 +289,20 @@ local function stanza_handler(event)
|
||||
if not room then
|
||||
-- this maybe as we receive the iq from jicofo after the room is already destroyed
|
||||
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
|
||||
|
||||
local processed;
|
||||
-- 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');
|
||||
if request_promotion then
|
||||
if not (room._connected_vnodes and room._connected_vnodes:get(stanza.attr.from)) then
|
||||
module:log('warn', 'Received forged promotion-request: %s %s %s', stanza, inspect(room._connected_vnodes), room._connected_vnodes:get(stanza.attr.from));
|
||||
if not from_vnode then
|
||||
module:log('warn', 'Received forged request_promotion message: %s %s',stanza, inspect(room._connected_vnodes));
|
||||
return true; -- stop processing
|
||||
end
|
||||
|
||||
@@ -249,6 +314,7 @@ local function stanza_handler(event)
|
||||
display_name,
|
||||
tonumber(request_promotion.attr.time),
|
||||
request_promotion.attr.userId,
|
||||
request_promotion.attr.groupId,
|
||||
request_promotion.attr.forcePromote
|
||||
);
|
||||
end
|
||||
@@ -266,6 +332,40 @@ local function stanza_handler(event)
|
||||
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
|
||||
module:log('warn', 'Unknown iq received for %s: %s', module.host, stanza);
|
||||
end
|
||||
@@ -275,6 +375,16 @@ local function stanza_handler(event)
|
||||
end
|
||||
|
||||
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
|
||||
|
||||
if DEBUG then
|
||||
module:log('debug', 'Processing promotion response for room %s, id %s, approved %s',
|
||||
room.jid, id, approved);
|
||||
end
|
||||
|
||||
-- lets reply to participant that requested promotion
|
||||
local username = new_id():lower();
|
||||
visitors_promotion_map[room.jid][username] = {
|
||||
@@ -308,11 +418,24 @@ end
|
||||
-- if room metadata does not have visitors.live set to `true` and there are no occupants in the meeting
|
||||
-- it will skip calling goLive endpoint
|
||||
local function go_live(room)
|
||||
|
||||
if DEBUG then
|
||||
module:log('debug', 'Checking if room %s is live', room.jid);
|
||||
end
|
||||
|
||||
if room._jitsi_go_live_sent then
|
||||
if DEBUG then
|
||||
module:log('debug', 'Room %s already sent go live request, skipping', room.jid);
|
||||
end
|
||||
return;
|
||||
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
|
||||
if DEBUG then
|
||||
module:log('debug', 'Room %s is not live, skipping go live request', room.jid);
|
||||
end
|
||||
return;
|
||||
end
|
||||
|
||||
@@ -326,6 +449,9 @@ local function go_live(room)
|
||||
|
||||
-- when there is an occupant then go live
|
||||
if not has_occupant then
|
||||
if DEBUG then
|
||||
module:log('debug', 'Room %s has no occupants, skipping go live request', room.jid);
|
||||
end
|
||||
return;
|
||||
end
|
||||
|
||||
@@ -367,6 +493,10 @@ process_host_module(muc_domain_prefix..'.'..muc_domain_base, function(host_modul
|
||||
local room, stanza, occupant, session = event.room, event.stanza, event.occupant, event.origin;
|
||||
|
||||
if is_healthcheck_room(room.jid) or is_admin(occupant.bare_jid) then
|
||||
if DEBUG then
|
||||
module:log('debug', 'Skipping visitor checks for healthcheck room %s or admin %s',
|
||||
room.jid, occupant.bare_jid);
|
||||
end
|
||||
return;
|
||||
end
|
||||
|
||||
@@ -378,19 +508,40 @@ process_host_module(muc_domain_prefix..'.'..muc_domain_base, function(host_modul
|
||||
join:tag('password', { xmlns = MUC_NS }):text(room:get_password());
|
||||
end
|
||||
|
||||
-- we skip any checks when auto-allow is enabled
|
||||
if auto_allow_promotion
|
||||
local is_live = get_visitors_room_metadata(room).live;
|
||||
|
||||
-- we skip any checks when auto-allow is enabled and room is live
|
||||
if (auto_allow_promotion or get_visitors_room_metadata(room).autoPromote and (is_live or is_live == nil))
|
||||
or ignore_list:contains(jid.host(stanza.attr.from)) -- jibri or other domains to ignore
|
||||
or is_sip_jigasi(stanza)
|
||||
or is_sip_jibri_join(stanza) then
|
||||
return;
|
||||
or is_sip_jibri_join(stanza)
|
||||
or table_find(room._data.moderators, session.jitsi_meet_context_user and session.jitsi_meet_context_user.id)
|
||||
or (room._data.moderator_id and room._data.moderator_id == (session.jitsi_meet_context_user and session.jitsi_meet_context_user.id))
|
||||
or table_find(room._data.participants, session.jitsi_meet_context_user and session.jitsi_meet_context_user.id) then
|
||||
if DEBUG then
|
||||
module:log('debug', 'Auto-allowing visitor %s in room %s', stanza.attr.from, room.jid);
|
||||
end
|
||||
return;
|
||||
end
|
||||
|
||||
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
|
||||
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
|
||||
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;
|
||||
end
|
||||
module:log('error', 'Visitor needs to be allowed by a moderator %s', stanza.attr.from);
|
||||
@@ -407,6 +558,16 @@ process_host_module(muc_domain_prefix..'.'..muc_domain_base, function(host_modul
|
||||
:tag('no-main-participants', { xmlns = 'jitsi:visitors' }));
|
||||
return true;
|
||||
end
|
||||
elseif room._data.participants then
|
||||
-- This is non jaas room which has a list of participants allowed to participate in the main room
|
||||
-- but this occupant is not one of them and the room is either not live or has no participants joined
|
||||
session.log('warn',
|
||||
'Deny user join in the main not live meeting, not in the list of main participants');
|
||||
session.send(st.error_reply(
|
||||
stanza, 'cancel', 'not-allowed',
|
||||
'Tried to join the main (not live or without main participants) room')
|
||||
:tag('not-live-room', { xmlns = 'jitsi:visitors' }));
|
||||
return true;
|
||||
end
|
||||
|
||||
end, 7); -- after muc_meeting_id, the logic for not joining before jicofo
|
||||
@@ -418,8 +579,16 @@ process_host_module(muc_domain_prefix..'.'..muc_domain_base, function(host_modul
|
||||
host_module:hook('muc-occupant-joined', function (event)
|
||||
local room, occupant = event.room, event.occupant;
|
||||
|
||||
if DEBUG then
|
||||
module:log('debug', 'Occupant %s joined room %s', occupant.jid, room.jid);
|
||||
end
|
||||
|
||||
if is_healthcheck_room(room.jid) or is_admin(occupant.bare_jid) or occupant.role ~= 'moderator' -- luacheck: ignore
|
||||
or not visitors_promotion_requests[event.room.jid] then
|
||||
if DEBUG then
|
||||
module:log('debug', 'Skipping visitor checks for healthcheck room %s or admin %s or not moderator %s',
|
||||
room.jid, occupant.bare_jid, occupant.role);
|
||||
end
|
||||
return;
|
||||
end
|
||||
|
||||
@@ -446,17 +615,11 @@ process_host_module(muc_domain_prefix..'.'..muc_domain_base, function(host_modul
|
||||
end
|
||||
end
|
||||
end);
|
||||
host_module:hook("message/bare", function(event)
|
||||
local stanza = event.stanza;
|
||||
|
||||
if stanza.attr.type ~= "groupchat" then
|
||||
return;
|
||||
end
|
||||
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);
|
||||
host_module:hook('jitsi-endpoint-message-received', function(event)
|
||||
local data, error, occupant, room, stanza
|
||||
= event.message, event.error, event.occupant, event.room, event.stanza;
|
||||
|
||||
if not data or data.type ~= 'visitors'
|
||||
or (data.action ~= "promotion-response" and data.action ~= "demote-request") then
|
||||
if error then
|
||||
@@ -465,17 +628,9 @@ process_host_module(muc_domain_prefix..'.'..muc_domain_base, function(host_modul
|
||||
return;
|
||||
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
|
||||
module:log('error', 'Occupant %s sending response message but not moderator in room %s',
|
||||
occupant_jid, room.jid);
|
||||
occupant.jid, room.jid);
|
||||
return false;
|
||||
end
|
||||
|
||||
@@ -501,7 +656,6 @@ process_host_module(muc_domain_prefix..'.'..muc_domain_base, function(host_modul
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
else
|
||||
if data.id then
|
||||
process_promotion_response(room, data.id, data.approved and 'true' or 'false');
|
||||
@@ -515,6 +669,7 @@ process_host_module(muc_domain_prefix..'.'..muc_domain_base, function(host_modul
|
||||
|
||||
return true; -- halt processing, but return true that we handled it
|
||||
end);
|
||||
|
||||
if visitors_queue_service then
|
||||
host_module:hook('muc-room-created', function (event)
|
||||
local room = event.room;
|
||||
@@ -535,7 +690,13 @@ process_host_module(muc_domain_prefix..'.'..muc_domain_base, function(host_modul
|
||||
go_live(event.room);
|
||||
end);
|
||||
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
|
||||
|
||||
@@ -571,3 +732,22 @@ prosody.events.add_handler('pre-jitsi-authentication', function(session)
|
||||
return session.customusername;
|
||||
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
|
||||
the main domain and we use it to subtract subdomains from the
|
||||
virtual addresses.
|
||||
The following confgurations are for multidomain setups and domain name
|
||||
The following configurations are for multidomain setups and domain name
|
||||
verification:
|
||||
--]]
|
||||
|
||||
@@ -225,7 +225,7 @@ function Util:get_public_key(keyId)
|
||||
self.cache:set(keyId, content);
|
||||
else
|
||||
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);
|
||||
end
|
||||
end
|
||||
|
@@ -1,7 +1,15 @@
|
||||
local http_server = require "net.http.server";
|
||||
local jid = require "util.jid";
|
||||
local st = require 'util.stanza';
|
||||
local timer = require "util.timer";
|
||||
local http = require "net.http";
|
||||
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 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 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 extract_subdomain_cache = cache.new(1000);
|
||||
@@ -122,11 +132,7 @@ function get_room_from_jid(room_jid)
|
||||
local component = hosts[host];
|
||||
if component then
|
||||
local muc = component.modules.muc
|
||||
if muc and rawget(muc,"rooms") 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)
|
||||
if muc then
|
||||
return muc.get_room_from_jid(room_jid);
|
||||
else
|
||||
return
|
||||
@@ -202,8 +208,7 @@ end
|
||||
-- @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
|
||||
-- a poltergeist), optional.
|
||||
function update_presence_identity(
|
||||
stanza, user, group, creator_user, creator_group)
|
||||
function update_presence_identity(stanza, user, group, creator_user, creator_group)
|
||||
|
||||
-- First remove any 'identity' element if it already
|
||||
-- exists, so it cannot be spoofed by a client
|
||||
@@ -216,7 +221,11 @@ function update_presence_identity(
|
||||
end
|
||||
return tag
|
||||
end
|
||||
)
|
||||
);
|
||||
|
||||
if not user then
|
||||
return;
|
||||
end
|
||||
|
||||
stanza:tag("identity"):tag("user");
|
||||
for k, v in pairs(user) do
|
||||
@@ -250,28 +259,33 @@ end
|
||||
-- Utility function to check whether feature is present and enabled. Allow
|
||||
-- a feature if there are features present in the session(coming from
|
||||
-- the token) and the value of the feature is true.
|
||||
-- If features is not present in the token we skip feature detection and allow
|
||||
-- everything.
|
||||
function is_feature_allowed(features, ft)
|
||||
if (features == nil or features[ft] == "true" or features[ft] == true) then
|
||||
return true;
|
||||
-- if features are missing from the token we check whether it is moderator
|
||||
function is_feature_allowed(ft, features, is_moderator)
|
||||
if features then
|
||||
return features[ft] == "true" or features[ft] == true;
|
||||
else
|
||||
return false;
|
||||
return is_moderator;
|
||||
end
|
||||
end
|
||||
|
||||
--- 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)
|
||||
local ret = extract_subdomain_cache:get(room_node);
|
||||
if ret then
|
||||
return ret.subdomain, ret.room;
|
||||
return ret.subdomain, ret.room, ret.customer_id;
|
||||
end
|
||||
|
||||
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);
|
||||
return subdomain, room_name;
|
||||
return subdomain, room_name, customer_id;
|
||||
end
|
||||
|
||||
function starts_with(str, start)
|
||||
@@ -282,19 +296,33 @@ function starts_with(str, start)
|
||||
end
|
||||
|
||||
function starts_with_one_of(str, prefixes)
|
||||
if not str then
|
||||
if not str or not prefixes then
|
||||
return false;
|
||||
end
|
||||
for i=1,#prefixes do
|
||||
if starts_with(str, prefixes[i]) then
|
||||
return prefixes[i];
|
||||
|
||||
if is_set(prefixes) then
|
||||
-- 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
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
function ends_with(str, ending)
|
||||
if not str then
|
||||
return false;
|
||||
end
|
||||
|
||||
return ending == "" or str:sub(-#ending) == ending
|
||||
end
|
||||
|
||||
@@ -468,9 +496,38 @@ end
|
||||
|
||||
-- Returns the initiator extension if the stanza is coming from a sip jigasi
|
||||
function is_sip_jigasi(stanza)
|
||||
if not stanza then
|
||||
return false;
|
||||
end
|
||||
|
||||
return stanza:get_child('initiator', 'http://jitsi.org/protocol/jigasi');
|
||||
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)
|
||||
if not email then
|
||||
return nil;
|
||||
@@ -508,6 +565,10 @@ function is_sip_jibri_join(stanza)
|
||||
return false
|
||||
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
|
||||
function process_host_module(name, callback)
|
||||
local function process_host(host)
|
||||
@@ -521,7 +582,7 @@ function process_host_module(name, callback)
|
||||
module:log('info', 'No host/component found, will wait for it: %s', name)
|
||||
|
||||
-- when a host or component is added
|
||||
prosody.events.add_handler('host-activated', process_host);
|
||||
prosody.events.add_handler('host-activated', process_host, -100); -- make sure everything is loaded
|
||||
else
|
||||
process_host(name);
|
||||
end
|
||||
@@ -535,30 +596,171 @@ function table_shallow_copy(t)
|
||||
return t2
|
||||
end
|
||||
|
||||
local function table_find(tab, val)
|
||||
if not tab or val == nil 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
|
||||
|
||||
-- Returns as a first result the removed items and as a second the added items
|
||||
local function table_compare(old_table, new_table)
|
||||
local removed = {}
|
||||
local added = {}
|
||||
local modified = {}
|
||||
|
||||
-- Find removed items (in old but not in new)
|
||||
for id, value in pairs(old_table) do
|
||||
if new_table[id] == nil then
|
||||
table.insert(removed, id)
|
||||
elseif new_table[id] ~= value then
|
||||
table.insert(modified, id)
|
||||
end
|
||||
end
|
||||
|
||||
-- Find added items (in new but not in old)
|
||||
for id, _ in pairs(new_table) do
|
||||
if old_table[id] == nil then
|
||||
table.insert(added, id)
|
||||
end
|
||||
end
|
||||
|
||||
return removed, added, modified
|
||||
end
|
||||
|
||||
local function table_equals(t1, t2)
|
||||
if t1 == nil then
|
||||
return t2 == nil;
|
||||
end
|
||||
if t2 == nil then
|
||||
return t1 == nil;
|
||||
end
|
||||
|
||||
local removed, added, modified = table_compare(t1, t2);
|
||||
|
||||
return next(removed) == nil and next(added) == nil and next(modified) == nil
|
||||
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
|
||||
|
||||
-- Filter out identity information (nick name, email, etc) from a presence stanza.
|
||||
local function filter_identity_from_presence(orig_stanza)
|
||||
local stanza = st.clone(orig_stanza);
|
||||
|
||||
stanza:remove_children('nick', 'http://jabber.org/protocol/nick');
|
||||
stanza:remove_children('email');
|
||||
stanza:remove_children('stats-id');
|
||||
local identity = stanza:get_child('identity');
|
||||
if identity then
|
||||
local user = identity:get_child('user');
|
||||
local name = identity:get_child('name');
|
||||
if user then
|
||||
user:remove_children('email');
|
||||
user:remove_children('name');
|
||||
end
|
||||
if name then
|
||||
name:remove_children('name'); -- Remove name with no namespace
|
||||
end
|
||||
end
|
||||
|
||||
return stanza;
|
||||
end
|
||||
|
||||
return {
|
||||
OUTBOUND_SIP_JIBRI_PREFIXES = OUTBOUND_SIP_JIBRI_PREFIXES;
|
||||
INBOUND_SIP_JIBRI_PREFIXES = INBOUND_SIP_JIBRI_PREFIXES;
|
||||
RECORDER_PREFIXES = RECORDER_PREFIXES;
|
||||
extract_subdomain = extract_subdomain;
|
||||
filter_identity_from_presence = filter_identity_from_presence;
|
||||
is_admin = is_admin;
|
||||
is_feature_allowed = is_feature_allowed;
|
||||
is_jibri = is_jibri;
|
||||
is_healthcheck_room = is_healthcheck_room;
|
||||
is_moderated = is_moderated;
|
||||
is_sip_jibri_join = is_sip_jibri_join;
|
||||
is_sip_jigasi = is_sip_jigasi;
|
||||
is_transcriber = is_transcriber;
|
||||
is_transcriber_jigasi = is_transcriber_jigasi;
|
||||
is_vpaas = is_vpaas;
|
||||
get_focus_occupant = get_focus_occupant;
|
||||
get_ip = get_ip;
|
||||
get_room_from_jid = get_room_from_jid;
|
||||
get_room_by_name_and_subdomain = get_room_by_name_and_subdomain;
|
||||
get_sip_jibri_email_prefix = get_sip_jibri_email_prefix;
|
||||
async_handler_wrapper = async_handler_wrapper;
|
||||
presence_check_status = presence_check_status;
|
||||
process_host_module = process_host_module;
|
||||
respond_iq_result = respond_iq_result;
|
||||
room_jid_match_rewrite = room_jid_match_rewrite;
|
||||
room_jid_split_subdomain = room_jid_split_subdomain;
|
||||
internal_room_jid_match_rewrite = internal_room_jid_match_rewrite;
|
||||
update_presence_identity = update_presence_identity;
|
||||
http_get_with_retry = http_get_with_retry;
|
||||
ends_with = ends_with;
|
||||
split_string = split_string;
|
||||
starts_with = starts_with;
|
||||
starts_with_one_of = starts_with_one_of;
|
||||
table_add = table_add;
|
||||
table_compare = table_compare;
|
||||
table_shallow_copy = table_shallow_copy;
|
||||
table_find = table_find;
|
||||
table_equals = table_equals;
|
||||
};
|
||||
|
@@ -3,6 +3,7 @@ plugin_paths = { "{{ jitsi_root_dir }}/prosody/modules" }
|
||||
|
||||
muc_mapper_domain_base = "{{ jitsi_domain }}";
|
||||
admins = { "{{ jitsi_jicofo_xmpp_user }}@{{ jitsi_auth_domain }}" };
|
||||
component_admins_as_room_owners = true;
|
||||
http_default_host = "{{ jitsi_domain }}";
|
||||
|
||||
-- Enable use of native prosody 0.11 support for epoll over select
|
||||
|
@@ -3,9 +3,9 @@
|
||||
jitsi_root_dir: /opt/jitsi
|
||||
jitsi_user: jitsi
|
||||
|
||||
jitsi_videobridge_version: "{{ jitsi_version | default('10133') }}"
|
||||
jitsi_videobridge_version: "{{ jitsi_version | default('10431') }}"
|
||||
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: 57d29c7d24c5f0657efbadabd54d3efa34c587a05c7a2196dca5e3c56141b44a
|
||||
|
||||
jitsi_videobridge_rtp_port: 10000
|
||||
jitsi_videobridge_src_ip:
|
||||
|
@@ -1,13 +1,13 @@
|
||||
---
|
||||
|
||||
# The version of ldap2pg to deploy
|
||||
ldap2pg_version: 6.2
|
||||
ldap2pg_version: '6.4.0'
|
||||
|
||||
# The URL where the ldap2pg archive can be fetched
|
||||
ldap2pg_archive_url: https://github.com/dalibo/ldap2pg/releases/download/v{{ ldap2pg_version }}/ldap2pg_{{ ldap2pg_version }}_linux_amd64.tar.gz
|
||||
|
||||
# The expected sha256 checksum of the archive
|
||||
ldap2pg_archive_sha256: 875fbee44c3831907e84fbc24cb80f6badccc33310c7b4cbe6346d9ac405f565
|
||||
ldap2pg_archive_sha256: 038301980136629ad6be4f7e0e5cb74bc41a8448d2320edaa7c9905fcc3e39e5
|
||||
|
||||
# How often ldap2pg will sync (eg hourly, '*:0:15', systemd timer syntaxe)
|
||||
ldap2pg_sync_freq: hourly
|
||||
|
@@ -1,15 +1,15 @@
|
||||
---
|
||||
|
||||
# Version to deploy
|
||||
metabase_version: 0.52.5
|
||||
metabase_version: 0.54.8
|
||||
# URL to fetch the jar
|
||||
metabase_jar_url: https://downloads.metabase.com/v{{ metabase_version }}.x/metabase.jar
|
||||
# Expected sha256 of the jar
|
||||
metabase_jar_sha256: 64fbbe0f1358dfcf7c2146270ca1e65dfc8a8972ab053df935340f6229009f91
|
||||
metabase_jar_sha256: c427ed7d45759f1a2c398c46fe73a9f1a67e6ba3c21ef67ab4a2e0e4231e6ab1
|
||||
# When building from source
|
||||
metabase_archive_url: https://github.com/metabase/metabase/archive/refs/tags/v{{ metabase_version }}.tar.gz
|
||||
# Expected sha256 of the archive
|
||||
metabase_archive_sha256: f30b43c208e0aaafa49e0b0172e1d78122ef0e7bad841400461018807cd55c5b
|
||||
metabase_archive_sha256: 399fb345e8eff956734721573769a866454ec3b2661e858a96d8f7ba115e33e5
|
||||
# Should ansible handle upgrades ? If set to false, only the initial install (and the config) will be handled
|
||||
metabase_manage_upgrade: True
|
||||
|
||||
@@ -31,6 +31,8 @@ metabase_db_server: "{{ (metabase_db_engine == 'mysql') | ternary(mysql_server,
|
||||
metabase_db_port: "{{ (metabase_db_engine == 'mysql') | ternary('3306', '5432') }}"
|
||||
metabase_db_name: metabase
|
||||
metabase_db_user: metabase
|
||||
# Should ansible try to create user and db on the server. If false, the user and the database must already exist
|
||||
metabase_db_create: true
|
||||
# A random pass will be generated and stored in the meta dir if not defined
|
||||
# metabase_db_pass: S3cr3t.
|
||||
|
||||
|
@@ -92,10 +92,14 @@
|
||||
- db_user: "{{ metabase_db_user }}"
|
||||
- db_server: "{{ metabase_db_server }}"
|
||||
- db_pass: "{{ metabase_db_pass }}"
|
||||
when: metabase_db_engine == 'mysql'
|
||||
when:
|
||||
- metabase_db_engine == 'mysql'
|
||||
- metabase_db_create
|
||||
tags: metabase
|
||||
|
||||
- when: metabase_db_engine == 'postgres'
|
||||
- when:
|
||||
- metabase_db_engine == 'postgres'
|
||||
- metabase_db_create
|
||||
block:
|
||||
- name: Install postgresql client
|
||||
package: name=postgresql16
|
||||
|
@@ -7,7 +7,7 @@ Type=simple
|
||||
User={{ metabase_user }}
|
||||
WorkingDirectory={{ metabase_root_dir }}/app
|
||||
EnvironmentFile={{ metabase_root_dir }}/etc/env
|
||||
ExecStart=/usr/bin/java -Djava.net.preferIPv4Stack=true \
|
||||
ExecStart=/usr/lib/jvm/jre-21/bin/java -Djava.net.preferIPv4Stack=true \
|
||||
-Djava.io.tmpdir={{ metabase_root_dir }}/tmp \
|
||||
{% if system_proxy is defined and system_proxy != '' %}
|
||||
-Dhttp.proxyHost={{ system_proxy | urlsplit('hostname') }} -Dhttp.proxyPort={{ system_proxy | urlsplit('port') }} \
|
||||
|
@@ -1,9 +1,9 @@
|
||||
---
|
||||
|
||||
metabase_packages:
|
||||
- java-11-openjdk
|
||||
- java-21-openjdk
|
||||
|
||||
metabase_build_packages:
|
||||
- java-11-openjdk-devel
|
||||
- java-21-openjdk-devel
|
||||
- patch
|
||||
- nodejs
|
||||
|
@@ -1,12 +1,5 @@
|
||||
---
|
||||
|
||||
- name: Create filebeat snippet direcories
|
||||
file: path=/etc/filebeat/ansible_{{ item }}.d state=directory
|
||||
loop:
|
||||
- inputs
|
||||
- modules
|
||||
tags: log,mkdir
|
||||
|
||||
- name: Create journald directory
|
||||
file: path=/var/log/journal state=directory
|
||||
notify: restart journald
|
||||
@@ -38,3 +31,7 @@
|
||||
- name: Create bash_completion dir
|
||||
file: path=/etc/bash_completion.d state=directory
|
||||
tags: mkdir
|
||||
|
||||
- name: Create vector config dir
|
||||
file: path=/etc/vector/conf.d state=directory
|
||||
tags: log,mkdir,vector
|
||||
|
@@ -33,3 +33,6 @@
|
||||
|
||||
- include_tasks: filebeat.yml
|
||||
tags: always
|
||||
|
||||
- include_tasks: vector.yml
|
||||
tags: always
|
||||
|
5
roles/nginx/tasks/vector.yml
Normal file
5
roles/nginx/tasks/vector.yml
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
|
||||
- name: Install vector configuration
|
||||
template: src=vector.yml.j2 dest=/etc/vector/conf.d/nginx.yml
|
||||
tags: log,web,vector
|
23
roles/nginx/templates/vector.yml.j2
Normal file
23
roles/nginx/templates/vector.yml.j2
Normal file
@@ -0,0 +1,23 @@
|
||||
---
|
||||
|
||||
sources:
|
||||
in_logs_nginx:
|
||||
type: file
|
||||
include:
|
||||
- /var/log/nginx/access.log
|
||||
- /var/log/nginx/error.log
|
||||
|
||||
transforms:
|
||||
format_logs_nginx:
|
||||
type: remap
|
||||
inputs:
|
||||
- in_logs_nginx
|
||||
source: |
|
||||
if (.file == "/var/log/nginx/access.log"){
|
||||
.http = parse_grok!(.message, "%{HOSTNAME:host} %{HTTPD_COMBINEDLOG}")
|
||||
} else if (.file == "/var/log/nginx/error.log"){
|
||||
.http = parse_nginx_log!(.message, format:"error")
|
||||
}
|
||||
.timestamp = parse_timestamp(del(.http.timestamp), format: "%d/%h/%Y:%H:%M:%S %z") ?? now()
|
||||
.service = "nginx"
|
||||
.group = "web"
|
@@ -3,11 +3,8 @@
|
||||
# List of plugins to install
|
||||
nomad_plugins:
|
||||
podman:
|
||||
archive_url: https://releases.hashicorp.com/nomad-driver-podman/0.6.2/nomad-driver-podman_0.6.2_linux_amd64.zip
|
||||
sha256: 49fc4c03864e0c1db6f2fde1369b432948fd0eda249a10e34c87ec3eb6e5870d
|
||||
containerd:
|
||||
archive_url: https://github.com/Roblox/nomad-driver-containerd/releases/download/v0.9.4/containerd-driver
|
||||
sha256: 337e1bab178071500bfbe46a59946e0e3bafc652906ed1b755d2aa4d35990982
|
||||
archive_url: https://releases.hashicorp.com/nomad-driver-podman/0.6.3/nomad-driver-podman_0.6.3_linux_amd64.zip
|
||||
sha256: 56b85d844385b4b5b96936fe6af06d623e87b5939f26de9fbad963f72af1e4a1
|
||||
exec2:
|
||||
archive_url: https://releases.hashicorp.com/nomad-driver-exec2/0.1.0/nomad-driver-exec2_0.1.0_linux_amd64.zip
|
||||
sha256: 13edd022ac793f3ca8cbd413015a13d7d396141d11234faaf72bdf34b08cae50
|
||||
|
@@ -66,3 +66,7 @@
|
||||
when: item.create | default(False)
|
||||
tags: nomad
|
||||
|
||||
- name: Create host_volumes_dir directory
|
||||
file: path={{ nomad_conf.client.host_volumes_dir }} state=directory owner={{ nomad_user }} group={{ nomad_user }} mode=770
|
||||
when: nomad_conf.client.host_volumes_dir is defined
|
||||
tags: nomad
|
||||
|
@@ -101,9 +101,11 @@ client {
|
||||
# Required for alloc to be able to reach themselves through an exposed port
|
||||
bridge_network_hairpin_mode = true
|
||||
|
||||
{% if nomad_conf.client.node_pool is defined %}
|
||||
node_pool = "{{ nomad_conf.client.node_pool }}"
|
||||
{% for conf in ['node_pool', 'host_volumes_dir'] %}
|
||||
{% if nomad_conf.client[conf] is defined %}
|
||||
{{ conf }} = "{{ nomad_conf.client[conf] }}"
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
|
||||
{% if nomad_conf.client.drain_on_shutdown is defined %}
|
||||
drain_on_shutdown {
|
||||
|
@@ -35,7 +35,7 @@ TasksMax=infinity
|
||||
OOMScoreAdjust=-1000
|
||||
{% if nomad_conf.client.enabled %}
|
||||
# Give Nomad some time to drain the node
|
||||
TimeoutStopSec=3600
|
||||
TimeoutStopSec=300
|
||||
{% endif %}
|
||||
|
||||
[Install]
|
||||
|
@@ -1,9 +1,9 @@
|
||||
---
|
||||
|
||||
# Version of Nomad to install
|
||||
nomad_version: 1.10.0
|
||||
nomad_version: 1.10.3
|
||||
# URL of the archive
|
||||
nomad_archive_url: https://releases.hashicorp.com/nomad/{{ nomad_version }}/nomad_{{ nomad_version }}_linux_amd64.zip
|
||||
# Expected sha256 of the archive
|
||||
nomad_archive_sha256: d0936673cfa026b87744d60ad21ba85db70fe792b0685bfce95ac06a98d30b9d
|
||||
nomad_archive_sha256: a161b8d59b42555d97d37f7a75c122831be485e89dfb97d16d6b60cfaec8d88b
|
||||
|
||||
|
@@ -53,3 +53,6 @@
|
||||
|
||||
- include_tasks: filebeat.yml
|
||||
tags: always
|
||||
|
||||
- include_tasks: vector.yml
|
||||
tags: always
|
||||
|
5
roles/pve/tasks/vector.yml
Normal file
5
roles/pve/tasks/vector.yml
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
|
||||
- name: Deploy vector configuration
|
||||
template: src=vector.yml dest=/etc/vector/conf.d/pve.yml
|
||||
tags: log,pve,vector
|
17
roles/pve/templates/vector.yml
Normal file
17
roles/pve/templates/vector.yml
Normal file
@@ -0,0 +1,17 @@
|
||||
---
|
||||
|
||||
sources:
|
||||
in_logs_pve:
|
||||
type: file
|
||||
include:
|
||||
- /var/log/pve-firewall.log
|
||||
- /var/log/pveproxy/access.log
|
||||
- /var/log/vzdump/*
|
||||
- /var/log/ceph/*.log
|
||||
|
||||
transforms:
|
||||
format_logs_pve:
|
||||
type: remap
|
||||
inputs: ["in_logs_pve"]
|
||||
source: |
|
||||
# Nothing to do
|
@@ -1,5 +1,5 @@
|
||||
[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
|
||||
gpgkey = https://download.postgresql.org/pub/repos/yum/keys/PGDG-RPM-GPG-KEY-RHEL
|
||||
name = PostgreSQL Client
|
||||
|
@@ -1,3 +1,3 @@
|
||||
---
|
||||
|
||||
nodejs_major_version: 18
|
||||
nodejs_major_version: 22
|
||||
|
@@ -1,7 +1,7 @@
|
||||
[nodejs]
|
||||
baseurl = https://rpm.nodesource.com/pub_{{ nodejs_major_version }}.x/el/{{ ansible_distribution_major_version }}/$basearch
|
||||
baseurl = https://rpm.nodesource.com/pub_{{ nodejs_major_version }}.x/nodistro/nodejs/$basearch
|
||||
gpgcheck = 1
|
||||
gpgkey = https://rpm.nodesource.com/pub/el/NODESOURCE-GPG-SIGNING-KEY-EL
|
||||
gpgkey = https://rpm.nodesource.com/gpgkey/ns-operations-public.key
|
||||
name = Node.js Packages for Enterprise Linux
|
||||
{% if ansible_os_family == 'RedHat' and ansible_distribution_major_version is version('8', '>=') %}
|
||||
# Workaround a bug in dnf which would make the default module mask
|
||||
|
3
roles/repo_vector/defaults/main.yml
Normal file
3
roles/repo_vector/defaults/main.yml
Normal file
@@ -0,0 +1,3 @@
|
||||
---
|
||||
|
||||
vector_major_version: 0
|
20
roles/repo_vector/tasks/Debian.yml
Normal file
20
roles/repo_vector/tasks/Debian.yml
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
|
||||
- name: Add Vector repo key
|
||||
apt_key:
|
||||
url: "{{ item }}"
|
||||
environment:
|
||||
https_proxy: "{{ system_proxy | default('') }}"
|
||||
loop:
|
||||
- https://keys.datadoghq.com/DATADOG_APT_KEY_CURRENT.public
|
||||
- https://keys.datadoghq.com/DATADOG_APT_KEY_C0962C7D.public
|
||||
- https://keys.datadoghq.com/DATADOG_APT_KEY_F14F620E.public
|
||||
tags: repo,log,vector
|
||||
|
||||
- name: Add Vector repo
|
||||
apt_repository:
|
||||
repo: deb https://apt.vector.dev/ stable vector-0
|
||||
filename: vector
|
||||
environment:
|
||||
https_proxy: "{{ system_proxy | default('') }}"
|
||||
tags: repo,log,vector
|
14
roles/repo_vector/tasks/RedHat.yml
Normal file
14
roles/repo_vector/tasks/RedHat.yml
Normal file
@@ -0,0 +1,14 @@
|
||||
---
|
||||
|
||||
- name: Configure vector repo
|
||||
yum_repository:
|
||||
name: vector
|
||||
description: Vector
|
||||
baseurl: https://yum.vector.dev/stable/vector-0/$basearch/
|
||||
gpgcheck: True
|
||||
gpgkey:
|
||||
- https://keys.datadoghq.com/DATADOG_RPM_KEY_CURRENT.public
|
||||
- https://keys.datadoghq.com/DATADOG_RPM_KEY_B01082D3.public
|
||||
- https://keys.datadoghq.com/DATADOG_RPM_KEY_FD4BF915.public
|
||||
priority: 1
|
||||
tags: repo,vector,log
|
4
roles/repo_vector/tasks/main.yml
Normal file
4
roles/repo_vector/tasks/main.yml
Normal file
@@ -0,0 +1,4 @@
|
||||
---
|
||||
|
||||
- include_tasks: "{{ ansible_os_family }}.yml"
|
||||
tags: always
|
@@ -7,6 +7,7 @@
|
||||
- el7-x86_64.cfg
|
||||
- el8-x86_64.cfg
|
||||
- el9-x86_64.cfg
|
||||
- el10-x86_64.cfg
|
||||
tags: rpm
|
||||
|
||||
- name: Deploy rpmmacros
|
||||
|
@@ -13,6 +13,7 @@
|
||||
- dir: "{{ rpm_root_dir }}/uploads/el7"
|
||||
- dir: "{{ rpm_root_dir }}/uploads/el8"
|
||||
- dir: "{{ rpm_root_dir }}/uploads/el9"
|
||||
- dir: "{{ rpm_root_dir }}/uploads/el10"
|
||||
- dir: "{{ rpm_root_dir }}/errors"
|
||||
- dir: "{{ rpm_root_dir }}/builds"
|
||||
- dir: "{{ rpm_root_dir }}/etc"
|
||||
|
@@ -7,6 +7,8 @@ targets:
|
||||
- x86_64
|
||||
el9:
|
||||
- x86_64
|
||||
el10:
|
||||
- x86_64
|
||||
|
||||
paths:
|
||||
repo: {{ rpm_root_dir }}/repo
|
||||
|
@@ -1,11 +1,14 @@
|
||||
module samba-dc 1.0;
|
||||
module samba-dc 1.1;
|
||||
|
||||
require {
|
||||
type ntpd_var_run_t;
|
||||
type unconfined_service_t;
|
||||
type chronyd_t;
|
||||
class sock_file write;
|
||||
class unix_stream_socket connectto;
|
||||
}
|
||||
|
||||
#============= chronyd_t ==============
|
||||
allow chronyd_t ntpd_var_run_t:sock_file write;
|
||||
allow chronyd_t unconfined_service_t:unix_stream_socket connectto;
|
||||
|
||||
|
@@ -16,3 +16,5 @@
|
||||
tags: always
|
||||
- include_tasks: filebeat.yml
|
||||
tags: always
|
||||
- include_tasks: vector.yml
|
||||
tags: always
|
||||
|
@@ -19,6 +19,7 @@
|
||||
when: samba_role == 'dc' or samba_role == 'rodc'
|
||||
with_items:
|
||||
- samba_domain_controller
|
||||
- rsync_sys_admin
|
||||
tags: samba
|
||||
|
||||
- name: Copy custom policy
|
||||
|
5
roles/samba/tasks/vector.yml
Normal file
5
roles/samba/tasks/vector.yml
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
|
||||
- name: Deploy vector configuration
|
||||
template: src=vector.yml dest=/etc/vector/conf.d/samba.yml
|
||||
tags: log,samba,vector
|
21
roles/samba/templates/vector.yml
Normal file
21
roles/samba/templates/vector.yml
Normal file
@@ -0,0 +1,21 @@
|
||||
---
|
||||
|
||||
sources:
|
||||
in_logs_samba:
|
||||
type: file
|
||||
include:
|
||||
- /var/log/samba/json/auth.log
|
||||
- /var/log/samba/json/dsdb.log
|
||||
- /var/log/samba/json/dsdb_password.log
|
||||
- /var/log/samba/json/dsdb_transaction.log
|
||||
|
||||
transforms:
|
||||
format_logs_samba:
|
||||
type: remap
|
||||
inputs: ["in_logs_samba"]
|
||||
source: |
|
||||
.message = string!(.message)
|
||||
if (is_json(.message)) {
|
||||
.samba = parse_json!(.message)
|
||||
.timestamp = parse_timestamp(del(.samba.timestamp), format: "%FT%H:%M:%S%.f%z") ?? now()
|
||||
}
|
@@ -4,6 +4,7 @@
|
||||
/usr/bin/systemctl stop seafile seahub
|
||||
{% endif %}
|
||||
/usr/bin/systemd-cat /usr/bin/sudo -u {{ seafile_user }} {{ seafile_root_dir }}/seafile-server/seaf-gc.sh
|
||||
/usr/bin/systemd-cat /usr/bin/sudo -u {{ seafile_user }} {{ seafile_root_dir }}/seafile-server/seaf-gc.sh --rm-fs
|
||||
{% if seafile_license is not defined %}
|
||||
/usr/bin/systemctl start seafile seahub
|
||||
{% endif %}
|
||||
|
@@ -10,3 +10,7 @@ almalinux.slaskdatacenter.com
|
||||
almalinux.mirror.katapult.io
|
||||
alma.mirror.ate.info
|
||||
mirror.previder.nl
|
||||
kitten.mirrors.almalinux.org
|
||||
linuxsoft.cern.ch
|
||||
mirrors.fibianet.dk
|
||||
mirror.bahnhof.net
|
||||
|
@@ -6,6 +6,7 @@ download.dokuwiki.org
|
||||
raw.githubusercontent.com
|
||||
objects.githubusercontent.com
|
||||
github-releases.githubusercontent.com
|
||||
release-assets.githubusercontent.com
|
||||
packagecloud.io
|
||||
.cloudfront.net
|
||||
packagist.org
|
||||
@@ -424,5 +425,6 @@ s3.eu-central-1.amazonaws.com
|
||||
# Vector.dev
|
||||
setup.vector.dev
|
||||
yum.vector.dev
|
||||
apt.vector.dev
|
||||
s3.amazonaws.com
|
||||
keys.datadoghq.com
|
||||
|
@@ -1,4 +1,4 @@
|
||||
module ufdb 1.3;
|
||||
module ufdb 1.4;
|
||||
|
||||
require {
|
||||
type initrc_tmp_t;
|
||||
@@ -6,6 +6,7 @@ require {
|
||||
type tmp_t;
|
||||
type squid_t;
|
||||
type unconfined_service_t;
|
||||
type var_run_t;
|
||||
class sock_file write;
|
||||
class unix_stream_socket connectto;
|
||||
}
|
||||
@@ -15,3 +16,4 @@ allow squid_t initrc_t:unix_stream_socket connectto;
|
||||
allow squid_t unconfined_service_t:unix_stream_socket connectto;
|
||||
allow squid_t initrc_tmp_t:sock_file write;
|
||||
allow squid_t tmp_t:sock_file write;
|
||||
allow squid_t var_run_t:sock_file write;
|
||||
|
@@ -226,3 +226,6 @@
|
||||
|
||||
- include_tasks: filebeat.yml
|
||||
tags: always
|
||||
|
||||
- include_tasks: vector.yml
|
||||
tags: always
|
||||
|
5
roles/squid/tasks/vector.yml
Normal file
5
roles/squid/tasks/vector.yml
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
|
||||
- name: Deploy vector configuration
|
||||
template: src=vector.yml dest=/etc/vector/conf.d/squid.yml
|
||||
tags: log,proxy,vector
|
25
roles/squid/templates/vector.yml
Normal file
25
roles/squid/templates/vector.yml
Normal file
@@ -0,0 +1,25 @@
|
||||
---
|
||||
|
||||
sources:
|
||||
in_logs_squid:
|
||||
type: file
|
||||
include:
|
||||
- /var/log/squid/access.log
|
||||
- /var/log/squid/cache.log
|
||||
- /var/log/squid/ufdbgclient.log
|
||||
- /var/log/ufdbguard/ufdbguardd.log
|
||||
|
||||
transforms:
|
||||
format_logs_squid:
|
||||
type: remap
|
||||
inputs: ["in_logs_squid"]
|
||||
source: |
|
||||
.group = "proxy"
|
||||
if (.file == "/var/log/squid/access.log"){
|
||||
.squid = parse_grok!(
|
||||
.message,
|
||||
"%{HTTPDATE:timestamp}\\s+%{NUMBER:response_time} %{IPORHOST:src_ip} %{NOTSPACE:squid_request_status}/%{NUMBER:http_status_code} %{NUMBER:transfer_size} %{NOTSPACE:http_method} (%{URIPROTO:url_scheme}://)?(?<url_host>\\S+?)(:%{INT:url_port})?(/%{NOTSPACE:url_path})?\\s+%{NOTSPACE:client_identity}\\s+%{NOTSPACE:peer_code}/%{NOTSPACE:peerhost}\\s+%{NOTSPACE:content_type}"
|
||||
)
|
||||
.timestamp = parse_timestamp(del(.squid.timestamp), format: "%d/%h/%Y:%H:%M:%S %z") ?? now()
|
||||
.service = "squid"
|
||||
}
|
@@ -25,3 +25,5 @@
|
||||
- include_tasks: cleanup.yml
|
||||
tags: always
|
||||
|
||||
- include_tasks: vector.yml
|
||||
tags: always
|
||||
|
5
roles/vault/tasks/vector.yml
Normal file
5
roles/vault/tasks/vector.yml
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
|
||||
- name: Deploy vector configuration
|
||||
template: src=vector.yml dest=/etc/vector/conf.d/vault.yml
|
||||
tags: log,vault,vector
|
18
roles/vault/templates/vector.yml
Normal file
18
roles/vault/templates/vector.yml
Normal file
@@ -0,0 +1,18 @@
|
||||
---
|
||||
|
||||
sources:
|
||||
in_logs_vault:
|
||||
type: file
|
||||
include:
|
||||
- /opt/vault/log/audit.json
|
||||
|
||||
transforms:
|
||||
format_logs_vault:
|
||||
type: remap
|
||||
inputs: ["in_logs_vault"]
|
||||
source: |
|
||||
.message = string!(.message)
|
||||
if (is_json(.message)) {
|
||||
.vault = parse_json!(.message)
|
||||
.timestamp = parse_timestamp(del(.vault.time), format: "%FT%H:%M:%S%.fZ", timezone: "UTC") ?? now()
|
||||
}
|
@@ -1,7 +1,7 @@
|
||||
# Version of Vault to install
|
||||
vault_version: 1.19.1
|
||||
vault_version: 1.20.1
|
||||
# URL of the archive
|
||||
vault_archive_url: https://releases.hashicorp.com/vault/{{ vault_version }}/vault_{{ vault_version }}_linux_amd64.zip
|
||||
# Expected sha256 of the archive
|
||||
vault_archive_sha256: a673933f5b02236b5e241e153c0d2fed15b47b48ad640ae886f8b3b567087a05
|
||||
vault_archive_sha256: e3ce3e678421c0d56f726952ab100875168c2e1eb1db751ed5a2b25b6b2ea96f
|
||||
|
||||
|
46
roles/vector/defaults/main.yml
Normal file
46
roles/vector/defaults/main.yml
Normal file
@@ -0,0 +1,46 @@
|
||||
---
|
||||
|
||||
vector_base_conf:
|
||||
data_dir: /var/lib/vector
|
||||
wildcard_matching: relaxed
|
||||
sources:
|
||||
in_logs_journald:
|
||||
type: journald
|
||||
|
||||
transforms:
|
||||
route_journald:
|
||||
type: route
|
||||
inputs: ["in_logs_journald"]
|
||||
route:
|
||||
dummy: exists(.dummy) && .dummy == "true"
|
||||
iptables: exists(._TRANSPORT) && ._TRANSPORT == "kernel" && starts_with(string!(.message), "Firewall:")
|
||||
|
||||
parse_journald_iptables:
|
||||
type: remap
|
||||
inputs: ["route_journald.iptables"]
|
||||
source: |
|
||||
msg = string!(.message)
|
||||
msg = replace(msg, "Firewall: ", "")
|
||||
.iptables = parse_key_value!(msg, whitespace:"strict")
|
||||
|
||||
format_logs_journald:
|
||||
type: remap
|
||||
inputs: ["route_journald._unmatched", "parse_journald_*"]
|
||||
source: |
|
||||
.group = "system"
|
||||
|
||||
logs_out:
|
||||
type: remap
|
||||
inputs: ['format_logs_*']
|
||||
source: |
|
||||
# Nothing to do
|
||||
|
||||
sinks:
|
||||
sink_blackhole:
|
||||
type: blackhole
|
||||
inputs:
|
||||
- logs_out
|
||||
|
||||
vector_extra_conf: {}
|
||||
vector_host_conf: {}
|
||||
vector_conf: "{{ vector_base_conf | combine(vector_extra_conf, recursive=true) | combine(vector_host_conf, recursive=true)}}"
|
7
roles/vector/handlers/main.yml
Normal file
7
roles/vector/handlers/main.yml
Normal file
@@ -0,0 +1,7 @@
|
||||
---
|
||||
|
||||
- name: reload vector
|
||||
service: name=vector state=reloaded
|
||||
|
||||
- name: restart vector
|
||||
service: name=vector state=restarted
|
5
roles/vector/meta/main.yml
Normal file
5
roles/vector/meta/main.yml
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
|
||||
dependencies:
|
||||
- role: mkdir
|
||||
- role: repo_vector
|
10
roles/vector/tasks/conf.yml
Normal file
10
roles/vector/tasks/conf.yml
Normal file
@@ -0,0 +1,10 @@
|
||||
---
|
||||
|
||||
- name: Deploy config
|
||||
template: src=vector.yml dest=/etc/vector/vector.yaml owner=root group=root mode=0600
|
||||
notify: reload vector
|
||||
tags: log,vector
|
||||
|
||||
- name: Remove dummy conf if present
|
||||
file: path=/etc/vector/conf.d/_dummy.yml state=absent
|
||||
tags: log,vector
|
1
roles/vector/tasks/facts.yml
Normal file
1
roles/vector/tasks/facts.yml
Normal file
@@ -0,0 +1 @@
|
||||
---
|
16
roles/vector/tasks/install.yml
Normal file
16
roles/vector/tasks/install.yml
Normal file
@@ -0,0 +1,16 @@
|
||||
---
|
||||
|
||||
- name: Install vector
|
||||
package: name=vector
|
||||
tags: log,vector
|
||||
|
||||
- name: Deploy custom systemd unit
|
||||
template: src=vector.service.j2 dest=/etc/systemd/system/vector.service
|
||||
register: vector_unit
|
||||
notify: restart vector
|
||||
tags: log,vector
|
||||
|
||||
- name: Reload systemd
|
||||
systemd: daemon_reload=true
|
||||
when: vector_unit.changed
|
||||
tags: log,vector
|
14
roles/vector/tasks/main.yml
Normal file
14
roles/vector/tasks/main.yml
Normal file
@@ -0,0 +1,14 @@
|
||||
---
|
||||
|
||||
- include_tasks: facts.yml
|
||||
tags: always
|
||||
|
||||
- include_tasks: install.yml
|
||||
tags: always
|
||||
|
||||
- include_tasks: conf.yml
|
||||
tags: always
|
||||
|
||||
- include_tasks: services.yml
|
||||
tags: always
|
||||
|
5
roles/vector/tasks/services.yml
Normal file
5
roles/vector/tasks/services.yml
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
|
||||
- name: Start and enable vector
|
||||
service: name=vector state=started enabled=true
|
||||
tags: log,vector
|
21
roles/vector/templates/vector.service.j2
Normal file
21
roles/vector/templates/vector.service.j2
Normal file
@@ -0,0 +1,21 @@
|
||||
[Unit]
|
||||
Description=Vector
|
||||
Documentation=https://vector.dev
|
||||
After=network-online.target
|
||||
Requires=network-online.target
|
||||
|
||||
[Service]
|
||||
ExecStartPre=/usr/bin/vector --config /etc/vector/vector.yaml --config-dir /etc/vector/conf.d validate
|
||||
ExecStart=/usr/bin/vector --config /etc/vector/vector.yaml --config-dir /etc/vector/conf.d --watch-config
|
||||
ExecReload=/usr/bin/vector --config /etc/vector/vector.yaml --config-dir /etc/vector/conf.d validate --no-environment
|
||||
ExecReload=/bin/kill -HUP $MAINPID
|
||||
Restart=always
|
||||
AmbientCapabilities=CAP_NET_BIND_SERVICE
|
||||
EnvironmentFile=-/etc/default/vector
|
||||
# Since systemd 229, should be in [Unit] but in order to support systemd <229,
|
||||
# it is also supported to have it here.
|
||||
StartLimitInterval=10
|
||||
StartLimitBurst=5
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
2
roles/vector/templates/vector.yml
Normal file
2
roles/vector/templates/vector.yml
Normal file
@@ -0,0 +1,2 @@
|
||||
---
|
||||
{{ vector_conf | to_nice_yaml(indent=2) }}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user