mirror of
https://git.lapiole.org/dani/ansible-roles.git
synced 2025-04-12 00:03:17 +02:00
192 lines
7.5 KiB
Lua
192 lines
7.5 KiB
Lua
-- A http endpoint to invite jigasi to a meeting via http endpoint
|
|
-- jwt is used to validate access
|
|
-- Copyright (C) 2023-present 8x8, Inc.
|
|
|
|
local jid_split = require "util.jid".split;
|
|
local hashes = require "util.hashes";
|
|
local random = require "util.random";
|
|
local st = require("util.stanza");
|
|
local json = require 'cjson.safe';
|
|
local util = module:require "util";
|
|
local async_handler_wrapper = util.async_handler_wrapper;
|
|
local process_host_module = util.process_host_module;
|
|
|
|
local muc_domain_base = module:get_option_string("muc_mapper_domain_base");
|
|
|
|
-- This module chooses jigasi from the brewery room, so it needs information for the configured brewery
|
|
local muc_domain = module:get_option_string("muc_internal_domain_base", 'internal.auth.' .. muc_domain_base);
|
|
|
|
local jigasi_brewery_room_jid = module:get_option_string("muc_jigasi_brewery_jid", 'jigasibrewery@' .. muc_domain);
|
|
|
|
local jigasi_bare_jid = module:get_option_string("muc_jigasi_jid", "jigasi@auth." .. muc_domain_base);
|
|
local focus_jid = module:get_option_string("muc_jicofo_brewery_jid", jigasi_brewery_room_jid .. "/focus");
|
|
|
|
local main_muc_service;
|
|
local JSON_CONTENT_TYPE = "application/json";
|
|
|
|
local event_count = module:measure("muc_invite_jigasi_rate", "rate")
|
|
local event_count_success = module:measure("muc_invite_jigasi_success", "rate")
|
|
local ASAP_KEY_SERVER = module:get_option_string("prosody_password_public_key_repo_url", "");
|
|
local token_util = module:require "token/util".new(module);
|
|
if ASAP_KEY_SERVER then
|
|
-- init token util with our asap keyserver
|
|
token_util:set_asap_key_server(ASAP_KEY_SERVER)
|
|
end
|
|
|
|
local function invite_jigasi(conference, phone_no)
|
|
local jigasi_brewery_room = main_muc_service.get_room_from_jid(jigasi_brewery_room_jid);
|
|
if not jigasi_brewery_room then
|
|
module:log("error", "Jigasi brewery room not found")
|
|
return 404, 'Brewery room was not found'
|
|
end
|
|
module:log("info", "Invite jigasi from %s to join conference %s and outbound phone_no %s", jigasi_brewery_room.jid, conference, phone_no)
|
|
|
|
--select least stressed Jigasi
|
|
local least_stressed_value = math.huge;
|
|
local least_stressed_jigasi_jid;
|
|
for occupant_jid, occupant in jigasi_brewery_room:each_occupant() do
|
|
local _, _, resource = jid_split(occupant_jid);
|
|
if resource ~= 'focus' then
|
|
local occ = occupant:get_presence();
|
|
local stats_child = occ:get_child("stats", "http://jitsi.org/protocol/colibri")
|
|
|
|
local is_sip_jigasi = true;
|
|
for stats_tag in stats_child:children() do
|
|
if stats_tag.attr.name == 'supports_sip' and stats_tag.attr.value == 'false' then
|
|
is_sip_jigasi = false;
|
|
end
|
|
end
|
|
|
|
if is_sip_jigasi then
|
|
for stats_tag in stats_child:children() do
|
|
if stats_tag.attr.name == 'stress_level' then
|
|
local stress_level = tonumber(stats_tag.attr.value);
|
|
module:log("debug", "Stressed level %s %s ", stress_level, occupant_jid)
|
|
if stress_level < least_stressed_value then
|
|
least_stressed_jigasi_jid = occupant_jid
|
|
least_stressed_value = stress_level
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
module:log("debug", "Least stressed jigasi selected jid %s value %s", least_stressed_jigasi_jid, least_stressed_value)
|
|
if not least_stressed_jigasi_jid then
|
|
module:log("error", "Cannot invite jigasi from room %s", jigasi_brewery_room.jid)
|
|
return 404, 'Jigasi not found'
|
|
end
|
|
|
|
-- invite Jigasi to join the conference
|
|
local _, _, jigasi_res = jid_split(least_stressed_jigasi_jid)
|
|
local jigasi_full_jid = jigasi_bare_jid .. "/" .. jigasi_res;
|
|
local stanza_id = hashes.sha256(random.bytes(8), true);
|
|
|
|
local invite_jigasi_stanza = st.iq({ xmlns = "jabber:client", type = "set", to = jigasi_full_jid, from = focus_jid, id = stanza_id })
|
|
:tag("dial", { xmlns = "urn:xmpp:rayo:1", from = "fromnumber", to = phone_no })
|
|
:tag("header", { xmlns = "urn:xmpp:rayo:1", name = "JvbRoomName", value = conference })
|
|
|
|
module:log("debug", "Invite jigasi stanza %s", invite_jigasi_stanza)
|
|
jigasi_brewery_room:route_stanza(invite_jigasi_stanza);
|
|
return 200
|
|
end
|
|
|
|
local function is_token_valid(token)
|
|
if token == nil then
|
|
module:log("warn", "no token provided");
|
|
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", tostring(reason), tostring(msg));
|
|
return false;
|
|
end
|
|
return true;
|
|
end
|
|
|
|
local function handle_jigasi_invite(event)
|
|
module:log("debug", "Request for invite jigasi received: reqId %s", event.request.headers["request_id"])
|
|
event_count()
|
|
local request = event.request;
|
|
-- verify access
|
|
local token = event.request.headers["authorization"]
|
|
if not token then
|
|
module:log("error", "Authorization header was not provided for conference %s", conference)
|
|
return { status_code = 401 };
|
|
end
|
|
if util.starts_with(token, 'Bearer ') then
|
|
token = token:sub(8, #token)
|
|
else
|
|
module:log("error", "Authorization header is invalid")
|
|
return { status_code = 401 };
|
|
end
|
|
if not is_token_valid(token) then
|
|
return { status_code = 401 };
|
|
end
|
|
|
|
-- verify payload
|
|
if request.headers.content_type ~= JSON_CONTENT_TYPE
|
|
or (not request.body or #request.body == 0) then
|
|
module:log("warn", "Wrong content type: %s or missing payload", request.headers.content_type);
|
|
return { status_code = 400; }
|
|
end
|
|
local payload, error = json.decode(request.body);
|
|
|
|
if not payload then
|
|
module:log('error', 'Cannot decode json error:%s', error);
|
|
return { status_code = 400; }
|
|
end
|
|
|
|
local conference = payload["conference"];
|
|
local phone_no = payload["phoneNo"];
|
|
if not conference then
|
|
module:log("warn", "Missing conference param")
|
|
return { status_code = 400; }
|
|
end
|
|
if not phone_no then
|
|
module:log("warn", "Missing phone no param")
|
|
return { status_code = 400; }
|
|
end
|
|
|
|
--invite jigasi
|
|
local status_code, error_msg = invite_jigasi(conference, phone_no)
|
|
|
|
if not error_msg then
|
|
event_count_success()
|
|
return { status_code = 200 }
|
|
else
|
|
return { status_code = status_code, body = json.encode({ error = error_msg }) }
|
|
end
|
|
end
|
|
|
|
module:log("info", "Adding http handler for /invite-jigasi on %s", module.host);
|
|
module:depends("http");
|
|
module:provides("http", {
|
|
default_path = "/";
|
|
route = {
|
|
["POST invite-jigasi"] = function(event)
|
|
return async_handler_wrapper(event, handle_jigasi_invite)
|
|
end;
|
|
};
|
|
});
|
|
|
|
process_host_module(muc_domain, function(_, host)
|
|
local muc_module = prosody.hosts[host].modules.muc;
|
|
if muc_module then
|
|
main_muc_service = muc_module;
|
|
module:log('info', 'Found main_muc_service: %s', main_muc_service);
|
|
else
|
|
module:log('info', 'Will wait for muc to be available');
|
|
prosody.hosts[host].events.add_handler('module-loaded', function(event)
|
|
if (event.module == 'muc') then
|
|
main_muc_service = prosody.hosts[host].modules.muc;
|
|
module:log('info', 'Found(on loaded) main_muc_service: %s', main_muc_service);
|
|
end
|
|
end);
|
|
end
|
|
end);
|
|
|