File

mod_audit/mod_audit.lua @ 5086:dec4b2e31d1c

mod_rest: Do not allow replies to <iq type=result> from webhooks Replying to a result stanza could cause loops, just as replying to an error. No stanza.name check here but it should be fine since no other kinds of stanzas have type=result, and the boolean logic would most definitely be wrong on the first attempt.
author Kim Alvefur <zash@zash.se>
date Sun, 27 Nov 2022 00:10:38 +0100
parent 4934:ae83200fb55f
child 5115:4a5837591380
line wrap: on
line source

module:set_global();

local time_now = os.time;
local st = require "util.stanza";
local moduleapi = require "core.moduleapi";

local host_wide_user = "@";

local stores = {};

local function get_store(self, host)
	local store = rawget(self, host);
	if store then
		return store
	end
	store = module:context(host):open_store("audit", "archive");
	rawset(self, host, store);
	return store;
end

setmetatable(stores, { __index = get_store });


local function session_extra(session)
	local attr = {
		xmlns = "xmpp:prosody.im/audit",
	};
	if session.id then
		attr.id = session.id;
	end
	if session.type then
		attr.type = session.type;
	end
	local stanza = st.stanza("session", attr);
	if session.ip then
		stanza:text_tag("remote-ip", session.ip);
	end
	return stanza
end

local function audit(host, user, source, event_type, extra)
	if not host or host == "*" then
		error("cannot log audit events for global");
	end
	local user_key = user or host_wide_user;

	local attr = {
		["source"] = source,
		["type"] = event_type,
	};
	if user_key ~= host_wide_user then
		attr.user = user_key;
	end
	local stanza = st.stanza("audit-event", attr);
	if extra ~= nil then
		if extra.session then
			local child = session_extra(extra.session);
			if child then
				stanza:add_child(child);
			end
		end
		if extra.custom then
			for _, child in extra.custom do
				if not st.is_stanza(child) then
					error("all extra.custom items must be stanzas")
				end
				stanza:add_child(child);
			end
		end
	end

	local id, err = stores[host]:append(nil, nil, stanza, time_now(), user_key);
	if err then
		module:log("error", "failed to persist audit event: %s", err);
		return
	else
		module:log("debug", "persisted audit event %s as %s", stanza:top_tag(), id);
	end
end

function moduleapi.audit(module, user, event_type, extra)
	audit(module.host, user, "mod_" .. module:get_name(), event_type, extra);
end

module:hook("audit", audit, 0);