mod_restrict_xmpp/mod_restrict_xmpp.lua
changeset 5013 459a4001c1d9
child 5014 a1f49586d28a
equal deleted inserted replaced
5012:bd63feda3704 5013:459a4001c1d9
       
     1 local array = require "util.array";
       
     2 local it = require "util.iterators";
       
     3 local set = require "util.set";
       
     4 local st = require "util.stanza";
       
     5 
       
     6 module:default_permission("prosody:user", "xmpp:federate");
       
     7 module:hook("route/remote", function (event)
       
     8 	if not module:may("xmpp:federate", event) then
       
     9 		if event.stanza.attr.type ~= "result" and event.stanza.attr.type ~= "error" then
       
    10 			module:log("warn", "Access denied: xmpp:federate for %s -> %s", event.stanza.attr.from, event.stanza.attr.to);
       
    11 			local reply = st.error_reply(event.stanza, "auth", "forbidden");
       
    12 			event.origin.send(reply);
       
    13 		end
       
    14 		return true;
       
    15 	end
       
    16 end);
       
    17 
       
    18 local iq_namespaces = {
       
    19 	["jabber:iq:roster"] = "contacts";
       
    20 	["jabber:iq:private"] = "storage";
       
    21 
       
    22 	["vcard-temp"] = "profile";
       
    23 	["urn:xmpp:mam:0"] = "history";
       
    24 	["urn:xmpp:mam:1"] = "history";
       
    25 	["urn:xmpp:mam:2"] = "history";
       
    26 
       
    27 	["urn:xmpp:carbons:0"] = "carbons";
       
    28 	["urn:xmpp:carbons:1"] = "carbons";
       
    29 	["urn:xmpp:carbons:2"] = "carbons";
       
    30 
       
    31 	["urn:xmpp:blocking"] = "blocklist";
       
    32 
       
    33 	["http://jabber.org/protocol/pubsub"] = "pep";
       
    34 	["http://jabber.org/protocol/disco#info"] = "disco";
       
    35 };
       
    36 
       
    37 local legacy_storage_nodes = {
       
    38 	["storage:bookmarks"] = "bookmarks";
       
    39 	["storage:rosternotes"] = "contacts";
       
    40 	["roster:delimiter"] = "contacts";
       
    41 	["storage:metacontacts"] = "contacts";
       
    42 };
       
    43 
       
    44 local pep_nodes = {
       
    45 	["storage:bookmarks"] = "bookmarks";
       
    46 	["urn:xmpp:bookmarks:1"] = "bookmarks";
       
    47 
       
    48 	["urn:xmpp:avatar:data"] = "profile";
       
    49 	["urn:xmpp:avatar:metadata"] = "profile";
       
    50 	["http://jabber.org/protocol/nick"] = "profile";
       
    51 
       
    52 	["eu.siacs.conversations.axolotl.devicelist"] = "omemo";
       
    53 	["urn:xmpp:omemo:1:devices"] = "omemo";
       
    54 	["urn:xmpp:omemo:1:bundles"] = "omemo";
       
    55 	["urn:xmpp:omemo:2:devices"] = "omemo";
       
    56 	["urn:xmpp:omemo:2:bundles"] = "omemo";
       
    57 };
       
    58 
       
    59 module:hook("pre-iq/bare", function (event)
       
    60 	if not event.to_self then return; end
       
    61 	local origin, stanza = event.origin, event.stanza;
       
    62 
       
    63 	local typ = stanza.attr.type;
       
    64 	if typ ~= "set" and typ ~= "get" then return; end
       
    65 	local action = typ == "get" and "read" or "write";
       
    66 
       
    67 	local payload = stanza.tags[1];
       
    68 	local ns = payload and payload.attr.xmlns;
       
    69 	local proto = iq_namespaces[ns];
       
    70 	if proto == "pep" then
       
    71 		local pubsub = payload:get_child("pubsub", "http://jabber.org/protocol/pubsub");
       
    72 		local node = pubsub and #pubsub.tags == 1 and pubsub.tags[1].attr.node or nil;
       
    73 		proto = pep_nodes[node] or "pep";
       
    74 		if proto == "pep" and node and node:match("^eu%.siacs%.conversations%.axolotl%.bundles%.%d+$") then
       
    75 			proto = "omemo"; -- COMPAT w/ original OMEMO
       
    76 		end
       
    77 	elseif proto == "storage" then
       
    78 		local data = payload.tags[1];
       
    79 		proto = data and legacy_storage_nodes[data.attr.xmlns] or "legacy-storage";
       
    80 	elseif proto == "carbons" then
       
    81 		-- This allows access to live messages
       
    82 		proto, action = "messages", "read";
       
    83 	end
       
    84 	local permission_name = "xmpp:account:"..(proto and (proto..":") or "")..action;
       
    85 	if not module:may(permission_name, event) then
       
    86 		module:log("warn", "Access denied: %s ({%s}%s) for %s", permission_name, ns, payload.name, origin.full_jid or origin.id);
       
    87 		origin.send(st.error_reply(stanza, "auth", "forbidden", "You do not have permission to make this request ("..permission_name..")"));
       
    88 		return true;
       
    89 	end
       
    90 end);
       
    91 
       
    92 --module:default_permission("prosody:restricted", "xmpp:account:read");
       
    93 --module:default_permission("prosody:restricted", "xmpp:account:write");
       
    94 module:default_permission("prosody:restricted", "xmpp:account:messages:read");
       
    95 module:default_permission("prosody:restricted", "xmpp:account:messages:write");
       
    96 for _, property_list in ipairs({ iq_namespaces, legacy_storage_nodes, pep_nodes }) do
       
    97 	for account_property in set.new(array.collect(it.values(property_list))) do
       
    98 		module:default_permission("prosody:restricted", "xmpp:account:"..account_property..":read");
       
    99 		module:default_permission("prosody:restricted", "xmpp:account:"..account_property..":write");
       
   100 	end
       
   101 end
       
   102 
       
   103 module:default_permission("prosody:restricted", "xmpp:account:presence:write");
       
   104 module:hook("pre-presence/bare", function (event)
       
   105 	if not event.to_self then return; end
       
   106 	local stanza = event.stanza;
       
   107 	if not module:may("xmpp:account:presence:write", event) then
       
   108 		module:log("warn", "Access denied: xmpp:account:presence:write for %s", event.origin.full_jid or event.origin.id);
       
   109 		event.origin.send(st.error_reply(stanza, "auth", "forbidden", "You do not have permission to send account presence"));
       
   110 		return true;
       
   111 	end
       
   112 	local priority = stanza:get_child_text("priority");
       
   113 	if priority ~= "-1" then
       
   114 		if not module:may("xmpp:account:messages:read", event) then
       
   115 			module:log("warn", "Access denied: xmpp:account:messages:read for %s", event.origin.full_jid or event.origin.id);
       
   116 			event.origin.send(st.error_reply(stanza, "auth", "forbidden", "You do not have permission to receive messages (use presence priority -1)"));
       
   117 			return true;
       
   118 		end
       
   119 	end
       
   120 end);