Mercurial > prosody-modules
view mod_report_affiliations/mod_report_affiliations.lua @ 6319:04c3273cb81f
mod_auth_cyrus: Add empty 'profile' table to SASL handler objects
This is for compatibility with Prosody's built-in util.sasl objects.
A SASL profile table usually includes methods supported by the backend, which
can be used by SASL mechanism handlers to perform operations (such as testing
the password). It also optionally contains a 'cb' field with channel binding
method handlers.
The Cyrus backend doesn't support channel binding, and doesn't have the same
concept of auth backend methods (it handles all that internally, and Prosody
has no insight or control over it).
Thus, we create an empty profile which informs Prosody that the SASL handler
does not support any of the auth or channel binding methods. Some features
will not work, but they didn't work anyway. This just makes it explicit.
This fixes a traceback in mod_sasl2_fast, which expected SASL handlers to
always contain a 'profile' field.
| author | Matthew Wild <mwild1@gmail.com> |
|---|---|
| date | Thu, 04 Sep 2025 10:14:46 +0100 |
| parents | e905ef16efb7 |
| children | a41d5d511948 |
line wrap: on
line source
local dt = require "util.datetime"; local jid = require "util.jid"; local st = require "util.stanza"; local rm = require "core.rostermanager"; local um = require "core.usermanager"; local traits = module:require("traits"); local xmlns_aff = "urn:xmpp:raa:0"; local is_host_anonymous = module:get_option_string("authentication") == "anonymous"; local trusted_servers = module:get_option_inherited_set("report_affiliations_trusted_servers", {}); local roles = { -- These affiliations are defined by XEP-0489, and we map a set of Prosody roles to each one admin = module:get_option_set("report_affiliations_admin_roles", { "prosody:admin", "prosody:operator" }); member = module:get_option_set("report_affiliations_member_roles", { "prosody:member" }); registered = module:get_option_set("report_affiliations_registered", { "prosody:user", "prosody:registered" }); guest = module:get_option_set("report_affiliations_anonymous", { "prosody:guest" }); }; -- Map of role to affiliation local role_affs = { -- [role (e.g. "prosody:guest")] = affiliation ("admin"|"member"|"registered"|"guest"); }; --Build the role->affiliation map based on the config for aff, aff_roles in pairs(roles) do for role in aff_roles do role_affs[role] = aff; end end local account_details_store = module:open_store("account_details"); local lastlog2_store = module:open_store("lastlog2"); module:add_feature(xmlns_aff); module:add_feature(xmlns_aff.."#embed-presence-sub"); module:add_feature(xmlns_aff.."#embed-presence-directed"); local function get_registered_timestamp(username) if um.get_account_info then local ts = um.get_account_info(username, module.host); if ts then return ts.created; end end local account_details = account_details_store:get(username); if account_details and account_details.registered then return account_details.registered; end local lastlog2 = lastlog2_store:get(username); if lastlog2 and lastlog2.registered then return lastlog2.registered.timestamp; end return nil; end local function get_trust_score(username) return math.floor(100 * (1 - traits.get_probability_bad(username))); end local function get_account_type(username) if is_host_anonymous then return "anonymous"; end if not um.get_user_role then return "registered"; -- COMPAT w/0.12 end local user_role = um.get_user_role(username, module.host); return role_affs[user_role] or "registered"; end function get_info_element(username) local account_type = get_account_type(username); local since, trust; if account_type == "registered" then since = get_registered_timestamp(username); trust = get_trust_score(username); end return st.stanza("info", { affiliation = account_type; since = since and dt.datetime(since - (since%86400)) or nil; trust = ("%d"):format(trust); xmlns = xmlns_aff; }); end -- Outgoing presence local function embed_in_outgoing_presence(pres_type) return function (event) local origin, stanza = event.origin, event.stanza; stanza:remove_children("info", xmlns_aff); -- Unavailable presence is pretty harmless, and blocking it may cause -- weird issues. if (pres_type == "bare" and stanza.attr.type == "unavailable") or (pres_type == "full" and stanza.attr.type ~= nil) then return; end -- Only attach info to stanzas sent to "strangers" (users that have not -- approved us to see their presence) if rm.is_user_subscribed(origin.username, origin.host, stanza.attr.to) then return; end local info = get_info_element(origin.username); if not info then return; end stanza:add_direct_child(info); end; end module:hook("pre-presence/bare", embed_in_outgoing_presence("bare")); module:hook("pre-presence/full", embed_in_outgoing_presence("full")); -- Handle direct queries local function should_permit_query(from_jid, to_username) --luacheck: ignore 212/to_username local from_node, from_host = jid.split(from_jid); if from_node then return false; end -- Who should we respond to? -- Only respond to domains -- Does user have a JID with this domain in directed presence? (doesn't work with bare JIDs) -- Does this user have a JID with domain in pending subscription requests? if trusted_servers:contains(from_host) then return true; end return false; end module:hook("iq-get/bare/urn:xmpp:raa:0:query", function (event) local origin, stanza = event.origin, event.stanza; local username = jid.node(stanza.attr.to); if not should_permit_query(stanza.attr.from, username) then origin.send(st.error_reply(stanza, "auth", "forbidden")); return true; end local info = get_info_element(username); local reply = st.reply(stanza) :add_child(info); origin.send(reply); return true; end);
