Merge pull request #1788 from jitsi/module-poltergeist

module-poltergeist
This commit is contained in:
Aaron van Meerten 2017-07-17 13:21:57 -05:00 committed by GitHub
commit 0c446026d6
5 changed files with 305 additions and 25 deletions

View File

@ -23,8 +23,12 @@ module:hook("bosh-session", function(event)
local query = request.url.query;
if query ~= nil then
session.auth_token = query and formdecode(query).token or nil;
end
local params = formdecode(query);
session.auth_token = query and params.token or nil;
-- The room name from the bosh query
session.jitsi_bosh_query_room = params.room;
end
end);
function provider.test_password(username, password)
@ -54,7 +58,18 @@ end
function provider.get_sasl_handler(session)
local function get_username_from_token(self, message)
return token_util:process_and_verify_token(session);
local res = token_util:process_and_verify_token(session);
local customUsername
= prosody.events.fire_event("pre-jitsi-authentication", session);
if (customUsername) then
self.username = customUsername;
else
self.username = message;
end
return res;
end
return new_sasl(host, { anonymous = get_username_from_token });
@ -69,8 +84,6 @@ local function anonymous(self, message)
-- This calls the handler created in 'provider.get_sasl_handler(session)'
local result, err, msg = self.profile.anonymous(self, username, self.realm);
self.username = username;
if result == true then
return "success";
else

View File

@ -0,0 +1,240 @@
local bare = require "util.jid".bare;
local generate_uuid = require "util.uuid".generate;
local jid = require "util.jid";
local neturl = require "net.url";
local parse = neturl.parseQuery;
local st = require "util.stanza";
local get_room_from_jid = module:require "util".get_room_from_jid;
-- Options
local poltergeist_component
= module:get_option_string("poltergeist_component", module.host);
-- table to store all poltergeists we create
local poltergeists = {};
-- poltergaist management functions
-- Returns the room if available, work and in multidomain mode
-- @param room_name the name of the room
-- @param group name of the group (optional)
-- @return returns room if found or nil
function get_room(room_name, group)
local room_address = jid.join(room_name, module:get_host());
-- if there is a group we are in multidomain mode
if group and group ~= "" then
room_address = "["..group.."]"..room_address;
end
return get_room_from_jid(room_address);
end
-- Stores the username in the table where we store poltergeist usernames
-- based on their room names
-- @param room the room instance
-- @param user_id the user id
-- @param username the username to store
function store_username(room, user_id, username)
local room_name = jid.node(room.jid);
-- we store in poltergeist user ids for room names
if (not poltergeists[room_name]) then
poltergeists[room_name] = {};
end
poltergeists[room_name][user_id] = username;
log("debug", "stored in session: %s", username);
end
-- Retrieve the username for a user
-- @param room the room instance
-- @param user_id the user id
-- @return returns the stored username for user or nil
function get_username(room, user_id)
local room_name = jid.node(room.jid);
if (not poltergeists[room_name]) then
return nil;
end
return poltergeists[room_name][user_id];
end
-- if we found that a session for a user with id has a poltergiest already
-- created, retrieve its jid and return it to the authentication
-- so we can reuse it and we that real user will replace the poltergiest
prosody.events.add_handler("pre-jitsi-authentication", function(session)
if (session.jitsi_meet_context_user) then
local room = get_room(
session.jitsi_bosh_query_room,
session.jitsi_meet_context_group);
if (not room) then
return nil;
end
local username
= get_username(room, session.jitsi_meet_context_user["id"]);
if (not username) then
return nil;
end
log("debug", "Found predefined username %s", username);
-- let's find the room and if the poltergeist occupant is there
-- lets remove him before the real participant joins
-- when we see the unavailable presence to go out the server
-- we will mark it with ignore tag
local nick = string.sub(username, 0, 8);
if (have_poltergeist_occupant(room, nick)) then
remove_poltergeist_occupant(room, nick);
end
return username;
end
return nil;
end);
-- Creates poltergeist occupant
-- @param room the room instance where we create the occupant
-- @param nick the nick to use for the new occupant
-- @param name the display name fot the occupant (optional)
-- @param avatar the avatar to use for the new occupant (optional)
function create_poltergeist_occupant(room, nick, name, avatar)
log("debug", "create_poltergeist_occupant %s:", nick);
-- Join poltergeist occupant to room, with the invited JID as their nick
local join_presence = st.presence({
to = room.jid.."/"..nick,
from = poltergeist_component.."/"..nick
}):tag("x", { xmlns = "http://jabber.org/protocol/muc" }):up();
if (name) then
join_presence:tag(
"nick",
{ xmlns = "http://jabber.org/protocol/nick" }):text(name):up();
end
if (avatar) then
join_presence:tag("avatar-url"):text(avatar):up();
end
room:handle_first_presence(
prosody.hosts[poltergeist_component], join_presence);
end
-- Removes poltergeist occupant
-- @param room the room instance where to remove the occupant
-- @param nick the nick of the occupant to remove
function remove_poltergeist_occupant(room, nick)
log("debug", "remove_poltergeist_occupant %s", nick);
local leave_presence = st.presence({
to = room.jid.."/"..nick,
from = poltergeist_component.."/"..nick,
type = "unavailable" });
room:handle_normal_presence(
prosody.hosts[poltergeist_component], leave_presence);
end
-- Checks for existance of a poltergeist occupant
-- @param room the room instance where to check for occupant
-- @param nick the nick of the occupant
-- @return true if occupant is found, false otherwise
function have_poltergeist_occupant(room, nick)
-- Find out if we have a poltergeist occupant in the room for this JID
return not not room:get_occupant_jid(poltergeist_component.."/"..nick);
end
-- Event handlers
--- Note: mod_muc and some of its sub-modules add event handlers between 0 and -100,
--- e.g. to check for banned users, etc.. Hence adding these handlers at priority -100.
module:hook("muc-decline", function (event)
remove_poltergeist_occupant(event.room, bare(event.stanza.attr.from));
end, -100);
-- before sending the presence for a poltergeist leaving add ignore tag
-- as poltergeist is leaving just before the real user joins and in the client
-- we ignore this presence to avoid leaving/joining experience and the real
-- user will reuse all currently created UI components for the same nick
module:hook("muc-broadcast-presence", function (event)
if (bare(event.occupant.jid) == poltergeist_component) then
if(event.stanza.attr.type == "unavailable") then
event.stanza:tag(
"ignore", { xmlns = "http://jitsi.org/jitmeet/" }):up();
end
end
end, -100);
--- Handles request for creating/managing poltergeists
-- @param event the http event, holds the request query
-- @return GET response, containing a json with response details
function handle_create_poltergeist (event)
local params = parse(event.request.url.query);
local user_id = params["user"];
local room_name = params["room"];
local group = params["group"];
local name = params["name"];
local avatar = params["avatar"];
local room = get_room(room_name, group);
if (not room) then
log("error", "no room found %s", room_address);
return 404;
end
local username = generate_uuid();
store_username(room, user_id, username)
create_poltergeist_occupant(room, string.sub(username,0,8), name, avatar);
return 200;
end
--- Handles request for updating poltergeists status
-- @param event the http event, holds the request query
-- @return GET response, containing a json with response details
function handle_update_poltergeist (event)
local params = parse(event.request.url.query);
local user_id = params["user"];
local room_name = params["room"];
local group = params["group"];
local status = params["status"];
local room = get_room(room_name, group);
if (not room) then
log("error", "no room found %s", room_address);
return 404;
end
local username = get_username(room, user_id);
if (not username) then
return 404;
end
local nick = string.sub(username, 0, 8);
if (have_poltergeist_occupant(room, nick)) then
local update_presence = st.presence({
to = room.jid.."/"..nick,
from = poltergeist_component.."/"..nick
}):tag("status"):text(status):up();
room:handle_normal_presence(
prosody.hosts[poltergeist_component], update_presence);
return 200;
else
return 404;
end
end
log("info", "Loading poltergeist service");
module:depends("http");
module:provides("http", {
default_path = "/";
name = "poltergeist";
route = {
["GET /poltergeist/create"] = handle_create_poltergeist;
["GET /poltergeist/update"] = handle_update_poltergeist;
};
});

View File

@ -19,6 +19,7 @@ local enableTokenVerification
= module:get_option_boolean("enable_roomsize_token_verification", false);
local token_util = module:require "token/util".new(module);
local get_room_from_jid = module:require "util".get_room_from_jid;
-- no token configuration but required
if token_util == nil and enableTokenVerification then
@ -31,26 +32,6 @@ end
local muc_domain_prefix
= module:get_option_string("muc_mapper_domain_prefix", "conference");
--- Finds and returns room by its jid
-- @param room_jid the room jid to search in the muc component
-- @return returns room if found or nil
function get_room_from_jid(room_jid)
local _, host = jid.split(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)
return muc.get_room_from_jid(room_jid);
else
return
end
end
end
--- Verifies room name, domain name with the values in the token
-- @param token the token we received
-- @param room_address the full room address jid

View File

@ -0,0 +1,21 @@
local st = require "util.stanza";
-- A component which we use to receive all stanzas for the created poltergeists
-- replays with error if an iq is sent
function no_action()
return true;
end
function error_reply(event)
module:send(st.error_reply(event.stanza, "cancel", "service-unavailable"));
return true;
end
module:hook("presence/host", no_action);
module:hook("message/host", no_action);
module:hook("presence/full", no_action);
module:hook("message/full", no_action);
module:hook("iq/host", error_reply);
module:hook("iq/full", error_reply);
module:hook("iq/bare", error_reply);

View File

@ -0,0 +1,25 @@
local jid = require "util.jid";
--- Finds and returns room by its jid
-- @param room_jid the room jid to search in the muc component
-- @return returns room if found or nil
function get_room_from_jid(room_jid)
local _, host = jid.split(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)
return muc.get_room_from_jid(room_jid);
else
return
end
end
end
return {
get_room_from_jid = get_room_from_jid;
};