-- Export a module:may() that works on Prosody 0.12 and earlier
-- (i.e. backed by is_admin).
-- This API is safe because Prosody 0.12 and earlier do not support
-- per-session roles - all authorization is based on JID alone. It is not
-- safe on versions that support per-session authorization.
module:set_global();
local moduleapi = require "core.moduleapi";
-- If module.may already exists, abort
if moduleapi.may then return; end
local jid_split = require "util.jid".split;
local um_is_admin = require "core.usermanager".is_admin;
local function get_jid_role_name(jid, host)
if um_is_admin(jid, "*") then
return "prosody:operator";
elseif um_is_admin(jid, host) then
return "prosody:admin";
end
return nil;
end
local function get_user_role_name(username, host)
return get_jid_role_name(username.."@"..host, host);
end
-- permissions[host][role_name][permission_name] = is_permitted
local permissions = {};
local role_inheritance = {
["prosody:operator"] = "prosody:admin";
["prosody:admin"] = "prosody:member";
["prosody:member"] = "prosody:registered";
["prosody:registered"] = "prosody:guest";
-- COMPAT
["prosody:user"] = "prosody:registered";
};
local function role_may(host, role_name, permission)
local host_roles = permissions[host];
if not host_roles then
return false;
end
local role_permissions = host_roles[role_name];
if not role_permissions then
return false;
end
local next_role = role_inheritance[role_name];
return not not permissions[role_name][permission] or (next_role and role_may(host, next_role, permission));
end
function moduleapi.may(self, action, context)
if action:byte(1) == 58 then -- action begins with ':'
action = self.name..action; -- prepend module name
end
if type(context) == "string" then -- check JID permissions
local role;
local node, host = jid_split(context);
if host == self.host then
role = get_user_role_name(node, self.host);
else
role = get_jid_role_name(context, self.host);
end
if not role then
self:log("debug", "Access denied: JID <%s> may not %s (no role found)", context, action);
return false;
end
local permit = role_may(self.host, role, action);
if not permit then
self:log("debug", "Access denied: JID <%s> may not %s (not permitted by role %s)", context, action, role.name);
end
return permit;
end
local session = context.origin or context.session;
if type(session) ~= "table" then
error("Unable to identify actor session from context");
end
if session.type == "s2sin" or (session.type == "c2s" and session.host ~= self.host) then
local actor_jid = context.stanza.attr.from;
local role_name = get_jid_role_name(actor_jid);
if not role_name then
self:log("debug", "Access denied: JID <%s> may not %s (no role found)", actor_jid, action);
return false;
end
local permit = role_may(self.host, role_name, action, context);
if not permit then
self:log("debug", "Access denied: JID <%s> may not %s (not permitted by role %s)", actor_jid, action, role_name);
end
return permit;
end
end
function moduleapi.default_permission(self, role_name, permission)
local p = permissions[self.host];
if not p then
p = {};
permissions[self.host] = p;
end
local r = p[role_name];
if not r then
r = {};
p[role_name] = r;
end
r[permission] = true;
end
function moduleapi.default_permissions(self, role_name, permission_list)
for _, permission in ipairs(permission_list) do
self:default_permission(role_name, permission);
end
end
function module.add_host(host_module)
permissions[host_module.host] = {};
function host_module.unload()
permissions[host_module.host] = nil;
end
end