# HG changeset patch
# User Trần H. Trung
# Date 1742229731 -25200
# Node ID e20901443eae8f0e4eecee5d37770dd93008dbab
# Parent 805515dd296081f687e68bf18b9afa559a3eaf33
Merge
diff -r 805515dd2960 -r e20901443eae .luacheckrc
--- a/.luacheckrc Wed Feb 26 19:36:35 2025 +0700
+++ b/.luacheckrc Mon Mar 17 23:42:11 2025 +0700
@@ -29,6 +29,7 @@
"module.hourly",
"module.broadcast",
"module.context",
+ "module.could",
"module.default_permission",
"module.default_permissions",
"module.depends",
@@ -83,6 +84,7 @@
}
globals = {
-- Methods that can be set on module API
+ "module.ready",
"module.unload",
"module.add_host",
"module.load",
diff -r 805515dd2960 -r e20901443eae misc/lnav/prosody.json
--- a/misc/lnav/prosody.json Wed Feb 26 19:36:35 2025 +0700
+++ b/misc/lnav/prosody.json Mon Mar 17 23:42:11 2025 +0700
@@ -33,7 +33,7 @@
"identifier" : true,
"kind" : "string"
},
- "payload" : {
+ "message" : {
"kind" : "xml"
}
}
diff -r 805515dd2960 -r e20901443eae mod_anti_spam/mod_anti_spam.lua
--- a/mod_anti_spam/mod_anti_spam.lua Wed Feb 26 19:36:35 2025 +0700
+++ b/mod_anti_spam/mod_anti_spam.lua Mon Mar 17 23:42:11 2025 +0700
@@ -4,7 +4,7 @@
local set = require "util.set";
local sha256 = require "util.hashes".sha256;
local st = require"util.stanza";
-local is_contact_subscribed = require "core.rostermanager".is_contact_subscribed;
+local rm = require "core.rostermanager";
local full_sessions = prosody.full_sessions;
local user_exists = require "core.usermanager".user_exists;
@@ -15,10 +15,30 @@
local spam_source_domains = set.new();
local spam_source_ips = trie.new();
local spam_source_jids = set.new();
+local default_spam_action = module:get_option("anti_spam_default_action", "bounce");
+local custom_spam_actions = module:get_option("anti_spam_actions", {});
+
+local spam_actions = setmetatable({}, {
+ __index = function (t, reason)
+ local action = rawget(custom_spam_actions, reason) or default_spam_action;
+ rawset(t, reason, action);
+ return action;
+ end;
+});
local count_spam_blocked = module:metric("counter", "anti_spam_blocked", "stanzas", "Stanzas blocked as spam", {"reason"});
+local hosts = prosody.hosts;
+
+local reason_messages = {
+ default = "Rejected as spam";
+ ["known-spam-source"] = "Rejected as spam. Your server is listed as a known source of spam. Please contact your server operator.";
+};
+
function block_spam(event, reason, action)
+ if not action then
+ action = spam_actions[reason];
+ end
event.spam_reason = reason;
event.spam_action = action;
if module:fire_event("spam-blocked", event) == false then
@@ -30,7 +50,7 @@
if action == "bounce" then
module:log("debug", "Bouncing likely spam %s from %s (%s)", event.stanza.name, event.stanza.attr.from, reason);
- event.origin.send(st.error_reply("cancel", "policy-violation", "Rejected as spam"));
+ event.origin.send(st.error_reply(event.stanza, "cancel", "policy-violation", reason_messages[reason] or reason_messages.default));
else
module:log("debug", "Discarding likely spam %s from %s (%s)", event.stanza.name, event.stanza.attr.from, reason);
end
@@ -47,13 +67,25 @@
local to_session = full_sessions[stanza.attr.to];
if to_session then return false; end
- if not is_contact_subscribed(to_user, to_host, from_jid) then
+ if not (
+ rm.is_contact_subscribed(to_user, to_host, from_jid) or
+ rm.is_user_subscribed(to_user, to_host, from_jid) or
+ rm.is_contact_pending_out(to_user, to_host, from_jid) or
+ rm.is_contact_preapproved(to_user, to_host, from_jid)
+ ) then
+ local from_user, from_host = jid_split(from_jid);
+
-- Allow all messages from your own jid
- if from_jid == to_user.."@"..to_host then
+ if from_user == to_user and from_host == to_host then
return false; -- Pass through
end
if to_resource and stanza.attr.type == "groupchat" then
- return false; -- Pass through
+ return false; -- Pass through group chat messages
+ end
+ if rm.is_contact_subscribed(to_user, to_host, from_host) then
+ -- If you have the sending domain in your roster,
+ -- allow through (probably a gateway)
+ return false;
end
return true; -- Stranger danger
end
@@ -63,8 +95,11 @@
if spam_source_domains:contains(session.from_host) then
return true;
end
- local origin_ip = ip.new(session.ip);
- if spam_source_ips:contains_ip(origin_ip) then
+ local raw_ip = session.ip;
+ local parsed_ip = raw_ip and ip.new_ip(session.ip);
+ -- Not every session has an ip - for example, stanzas sent from a
+ -- local host session
+ if parsed_ip and spam_source_ips:contains_ip(parsed_ip) then
return true;
end
end
@@ -82,6 +117,8 @@
if not (spammy_strings or spammy_patterns) then return; end
local body = stanza:get_child_text("body");
+ if not body then return; end
+
if spammy_strings then
for _, s in ipairs(spammy_strings) do
if body:find(s, 1, true) then
@@ -100,7 +137,7 @@
-- Set up RTBLs
-local anti_spam_services = module:get_option_array("anti_spam_services");
+local anti_spam_services = module:get_option_array("anti_spam_services", {});
for _, rtbl_service_jid in ipairs(anti_spam_services) do
new_rtbl_subscription(rtbl_service_jid, "spam_source_domains", {
@@ -113,10 +150,18 @@
});
new_rtbl_subscription(rtbl_service_jid, "spam_source_ips", {
added = function (item)
- spam_source_ips:add_subnet(ip.parse_cidr(item));
+ local subnet_ip, subnet_bits = ip.parse_cidr(item);
+ if not subnet_ip then
+ return;
+ end
+ spam_source_ips:add_subnet(subnet_ip, subnet_bits);
end;
removed = function (item)
- spam_source_ips:remove_subnet(ip.parse_cidr(item));
+ local subnet_ip, subnet_bits = ip.parse_cidr(item);
+ if not subnet_ip then
+ return;
+ end
+ spam_source_ips:remove_subnet(subnet_ip, subnet_bits);
end;
});
new_rtbl_subscription(rtbl_service_jid, "spam_source_jids_sha256", {
@@ -130,36 +175,65 @@
end
module:hook("message/bare", function (event)
- local to_bare = jid_bare(event.stanza.attr.to);
+ local to_user, to_host = jid_split(event.stanza.attr.to);
- if not user_exists(to_bare) then return; end
+ if not hosts[to_host] then
+ module:log("warn", "Skipping filtering of message to unknown host <%s>", to_host);
+ return;
+ end
local from_bare = jid_bare(event.stanza.attr.from);
- if not is_from_stranger(from_bare, event) then return; end
+ if user_exists(to_user, to_host) then
+ if not is_from_stranger(from_bare, event) then
+ return;
+ end
+ end
+
+ module:log("debug", "Processing message from stranger...");
if is_spammy_server(event.origin) then
- return block_spam(event, "known-spam-source", "drop");
+ return block_spam(event, "known-spam-source");
end
if is_spammy_sender(from_bare) then
- return block_spam(event, "known-spam-jid", "drop");
+ return block_spam(event, "known-spam-jid");
end
if is_spammy_content(event.stanza) then
- return block_spam(event, "spam-content", "drop");
+ return block_spam(event, "spam-content");
end
+
+ module:log("debug", "Allowing message through");
end, 500);
module:hook("presence/bare", function (event)
- if event.stanza.type ~= "subscribe" then
+ if event.stanza.attr.type ~= "subscribe" then
return;
end
- if is_spammy_server(event.origin) then
- return block_spam(event, "known-spam-source", "drop");
+
+ local to_user, to_host = jid_split(event.stanza.attr.to);
+ local from_bare = jid_bare(event.stanza.attr.from);
+
+ if user_exists(to_user, to_host) then
+ if not is_from_stranger(from_bare, event) then
+ return;
+ end
end
- if is_spammy_sender(event.stanza) then
- return block_spam(event, "known-spam-jid", "drop");
+ module:log("debug", "Processing subscription request from stranger...");
+
+ if is_spammy_server(event.origin) then
+ return block_spam(event, "known-spam-source");
end
+
+ module:log("debug", "Not from known spam source server");
+
+ if is_spammy_sender(jid_bare(event.stanza.attr.from)) then
+ return block_spam(event, "known-spam-jid");
+ end
+
+ module:log("debug", "Not from known spam source JID");
+
+ module:log("debug", "Allowing subscription request through");
end, 500);
diff -r 805515dd2960 -r e20901443eae mod_anti_spam/trie.lib.lua
--- a/mod_anti_spam/trie.lib.lua Wed Feb 26 19:36:35 2025 +0700
+++ b/mod_anti_spam/trie.lib.lua Mon Mar 17 23:42:11 2025 +0700
@@ -1,4 +1,4 @@
-local bit = require "prosody.util.bitcompat";
+local bit = require "util.bitcompat";
local trie_methods = {};
local trie_mt = { __index = trie_methods };
@@ -120,7 +120,7 @@
end
end
-function trie_methods:has_ip(item)
+function trie_methods:contains_ip(item)
item = item.packed;
local node = self.root;
local len = #item;
diff -r 805515dd2960 -r e20901443eae mod_audit/README.md
--- a/mod_audit/README.md Wed Feb 26 19:36:35 2025 +0700
+++ b/mod_audit/README.md Mon Mar 17 23:42:11 2025 +0700
@@ -49,3 +49,9 @@
```shell
prosodyctl mod_audit user@example.com
```
+
+# Compatibilty
+
+Requires Prosody **trunk** as of 2025-02-11.
+
+Does not work with Prosody 0.12 or earlier.
diff -r 805515dd2960 -r e20901443eae mod_auth_oauth_external/README.md
--- a/mod_auth_oauth_external/README.md Wed Feb 26 19:36:35 2025 +0700
+++ b/mod_auth_oauth_external/README.md Mon Mar 17 23:42:11 2025 +0700
@@ -11,12 +11,14 @@
# How it works
-Clients retrieve tokens somehow, then show them to Prosody, which asks
-the Authorization server to validate them, returning info about the user
-back to Prosody.
+Using OAuth 2.0 in XMPP is explained in [XEP-0493: OAuth Client Login].
+Clients pass tokens from the Authorization Server to Prosody, which
+attempts to validate the tokens using the configured validation
+endpoint.
-Alternatively for legacy clients, Prosody receives the users username
-and password and retrieves a token itself, then proceeds as above.
+Legacy clients have to use SASL PLAIN, where Prosody receives the users
+username and password and attempts to validate this using the OAuth 2
+resource owner password grant.
# Configuration
diff -r 805515dd2960 -r e20901443eae mod_auth_oauth_external/mod_auth_oauth_external.lua
--- a/mod_auth_oauth_external/mod_auth_oauth_external.lua Wed Feb 26 19:36:35 2025 +0700
+++ b/mod_auth_oauth_external/mod_auth_oauth_external.lua Mon Mar 17 23:42:11 2025 +0700
@@ -58,7 +58,7 @@
function provider.get_sasl_handler()
local profile = {};
- profile.http_client = http.new({ connection_pooling = true }); -- TODO configurable
+ profile.http_client = http.default:new({ connection_pooling = true }); -- TODO configurable
local extra = { oidc_discovery_url = oidc_discovery_url };
if token_endpoint and allow_plain then
local map_username = function (username, _realm) return username; end; --jid.join; -- TODO configurable
diff -r 805515dd2960 -r e20901443eae mod_client_certs/mod_client_certs.lua
--- a/mod_client_certs/mod_client_certs.lua Wed Feb 26 19:36:35 2025 +0700
+++ b/mod_client_certs/mod_client_certs.lua Mon Mar 17 23:42:11 2025 +0700
@@ -10,7 +10,7 @@
local dm_load = require "util.datamanager".load;
local dm_store = require "util.datamanager".store;
local dm_table = "client_certs";
-local ssl_x509 = require "ssl.x509";
+local ssl = require "ssl";
local util_x509 = require "util.x509";
local id_on_xmppAddr = "1.3.6.1.5.5.7.8.5";
local id_ce_subjectAltName = "2.5.29.17";
@@ -141,7 +141,7 @@
local can_manage = append:get_child("no-cert-management", xmlns_saslcert) ~= nil;
x509cert = x509cert:gsub("^%s*(.-)%s*$", "%1");
- local cert = ssl_x509.load(util_x509.der2pem(base64.decode(x509cert)));
+ local cert = ssl.loadcertificate(util_x509.der2pem(base64.decode(x509cert)));
if not cert then
origin.send(st.error_reply(stanza, "modify", "not-acceptable", "Could not parse X.509 certificate"));
@@ -206,8 +206,8 @@
instructions = "What action do you want to perform?";
{ name = "FORM_TYPE", type = "hidden", value = "http://prosody.im/protocol/certs#subcmd" };
- { name = "subcmd", type = "list-single", label = "Actions", required = true,
- value = { {label = "Add certificate", value = "add"},
+ { name = "subcmd", type = "list-single", label = "Actions", required = false,
+ options = { {label = "Add certificate", value = "add"},
{label = "List certificates", value = "list"},
{label = "Disable certificate", value = "disable"},
{label = "Revoke certificate", value = "revoke"},
@@ -292,7 +292,7 @@
local name = fields.name;
local x509cert = fields.cert:gsub("^%s*(.-)%s*$", "%1");
- local cert = ssl_x509.load(util_x509.der2pem(base64.decode(x509cert)));
+ local cert = ssl.loadcertificate(x509cert);
if not cert then
return { status = "completed", error = { message = "Could not parse X.509 certificate" } };
@@ -327,7 +327,7 @@
end
end
-local cmd_desc = adhoc_new("Manage certificates", "http://prosody.im/protocol/certs", adhoc_handler, "user");
+local cmd_desc = adhoc_new("Manage certificates", "http://prosody.im/protocol/certs", adhoc_handler, "any");
module:provides("adhoc", cmd_desc);
-- Here comes the SASL EXTERNAL stuff
diff -r 805515dd2960 -r e20901443eae mod_cloud_notify/mod_cloud_notify.lua
--- a/mod_cloud_notify/mod_cloud_notify.lua Wed Feb 26 19:36:35 2025 +0700
+++ b/mod_cloud_notify/mod_cloud_notify.lua Mon Mar 17 23:42:11 2025 +0700
@@ -4,6 +4,9 @@
--
-- This file is MIT/X11 licensed.
+-- This module is only for 0.12, later versions have mod_cloud_notify bundled
+--% conflicts: mod_cloud_notify
+
local os_time = os.time;
local st = require"util.stanza";
local jid = require"util.jid";
diff -r 805515dd2960 -r e20901443eae mod_cloud_notify_encrypted/README.md
--- a/mod_cloud_notify_encrypted/README.md Wed Feb 26 19:36:35 2025 +0700
+++ b/mod_cloud_notify_encrypted/README.md Mon Mar 17 23:42:11 2025 +0700
@@ -2,9 +2,6 @@
labels:
- 'Stage-Alpha'
summary: 'Support for encrypted payloads in push notifications'
-rockspec:
- dependencies:
- - mod_cloud_notify
...
Introduction
diff -r 805515dd2960 -r e20901443eae mod_cloud_notify_encrypted/mod_cloud_notify_encrypted.lua
--- a/mod_cloud_notify_encrypted/mod_cloud_notify_encrypted.lua Wed Feb 26 19:36:35 2025 +0700
+++ b/mod_cloud_notify_encrypted/mod_cloud_notify_encrypted.lua Mon Mar 17 23:42:11 2025 +0700
@@ -144,5 +144,6 @@
module:log("debug", "Encrypted '%s' push notification using %s", push_payload.type, encryption.algorithm);
end
+module:depends("cloud_notify");
module:hook("cloud_notify/registration", handle_register);
module:hook("cloud_notify/push", handle_push, 1);
diff -r 805515dd2960 -r e20901443eae mod_cloud_notify_filters/README.md
--- a/mod_cloud_notify_filters/README.md Wed Feb 26 19:36:35 2025 +0700
+++ b/mod_cloud_notify_filters/README.md Mon Mar 17 23:42:11 2025 +0700
@@ -2,9 +2,6 @@
labels:
- 'Stage-Alpha'
summary: 'Support for push notification filtering rules'
-rockspec:
- dependencies:
- - mod_cloud_notify
...
Introduction
diff -r 805515dd2960 -r e20901443eae mod_cloud_notify_priority_tag/README.md
--- a/mod_cloud_notify_priority_tag/README.md Wed Feb 26 19:36:35 2025 +0700
+++ b/mod_cloud_notify_priority_tag/README.md Mon Mar 17 23:42:11 2025 +0700
@@ -2,9 +2,6 @@
labels:
- 'Stage-Alpha'
summary: 'Support for indicating importance to push notification servers'
-rockspec:
- dependencies:
- - mod_cloud_notify
...
Introduction
diff -r 805515dd2960 -r e20901443eae mod_conversejs/mod_conversejs.lua
--- a/mod_conversejs/mod_conversejs.lua Wed Feb 26 19:36:35 2025 +0700
+++ b/mod_conversejs/mod_conversejs.lua Mon Mar 17 23:42:11 2025 +0700
@@ -126,7 +126,7 @@
module:provides("http", {
title = "Converse.js";
route = {
- GET = function (event)
+ ["GET /"] = function (event)
local converse_options = get_converse_options();
event.response.headers.content_type = "text/html";
@@ -175,7 +175,7 @@
sizes = "512x512",
},
}),
- start_url = module:http_url(),
+ start_url = module:http_url().."/",
background_color = pwa_color,
display = "standalone",
scope = module:http_url().."/",
diff -r 805515dd2960 -r e20901443eae mod_csi_muc_priorities/mod_csi_muc_priorities.lua
--- a/mod_csi_muc_priorities/mod_csi_muc_priorities.lua Wed Feb 26 19:36:35 2025 +0700
+++ b/mod_csi_muc_priorities/mod_csi_muc_priorities.lua Mon Mar 17 23:42:11 2025 +0700
@@ -12,14 +12,6 @@
local username = session.username;
local priorities = user_sessions[username].csi_muc_priorities;
- if priorities then
- local priority = priorities[room_jid];
- if priority ~= nil then
- event.reason = "muc priority";
- return priority;
- end
- end
-
-- Look for mention
local rooms = session.rooms_joined;
if rooms then
@@ -33,12 +25,33 @@
end
-- Your own messages
if stanza.attr.from == (room_jid .. "/" .. room_nick) then
- event.reason = "muc own message";
+ event.reason = "muc own message";
return true;
end
end
end
+ -- No mentions found, check other logic:
+ -- deflaultlow=f or nil defaultlow=t
+ -- in high prio nil nil
+ -- in low prio false false
+ -- not in either nil false
+ --
+ -- true means: important (always send immediately)
+ -- nil means: normal (respect other mods for stuff like grace period/reactions/etc)
+ -- false means: unimportant (delay sending)
+ if priorities then
+ local priority = priorities[room_jid];
+ if priority == false then -- low priority
+ event.reason = "muc priority";
+ return false;
+ end
+ if priorities[false] and priorities[false]["defaultlow"] and not priority then -- defaultlow is false or nil or not high priority
+ event.reason = "muc user default low";
+ return false;
+ end
+ end
+
-- Standard importance and no mention, leave to other modules to decide for now
return nil;
end
@@ -74,6 +87,12 @@
label = "Lower priority";
desc = "E.g. large noisy public channels";
};
+ {
+ type = "boolean";
+ name = "defaultlow";
+ label = "Default to lower priority";
+ desc = "Mark all channels lower priority as default";
+ };
}
local store = module:open_store();
@@ -87,20 +106,29 @@
local prioritized_jids = user_sessions[username].csi_muc_priorities or store:get(username);
local important = {};
local unimportant = {};
+ local defaultlow = false; -- Default to high priority
if prioritized_jids then
for jid, priority in pairs(prioritized_jids) do
- if priority then
- table.insert(important, jid);
- else
- table.insert(unimportant, jid);
+ if jid then
+ if priority then
+ table.insert(important, jid);
+ else
+ table.insert(unimportant, jid);
+ end
end
end
table.sort(important);
table.sort(unimportant);
+
+ if prioritized_jids[false] then
+ defaultlow = prioritized_jids[false]["defaultlow"];
+ end
end
+
return {
important = important;
unimportant = unimportant;
+ defaultlow = defaultlow
};
end, function(fields, form_err, data)
if form_err then
@@ -108,17 +136,18 @@
end
local prioritized_jids = {};
if fields.unimportant then
- if fields.unimportant then
- for _, jid in ipairs(fields.unimportant) do
- prioritized_jids[jid] = false;
- end
- end
- if fields.important then
- for _, jid in ipairs(fields.important) do
- prioritized_jids[jid] = true;
- end
+ for _, jid in ipairs(fields.unimportant) do
+ prioritized_jids[jid] = false;
end
end
+ if fields.important then
+ for _, jid in ipairs(fields.important) do
+ prioritized_jids[jid] = true;
+ end
+ end
+
+ local misc_data = {defaultlow = fields.defaultlow};
+ prioritized_jids[false] = misc_data;
local username = jid_split(data.from);
local ok, err = store:set(username, prioritized_jids);
diff -r 805515dd2960 -r e20901443eae mod_firewall/conditions.lib.lua
--- a/mod_firewall/conditions.lib.lua Wed Feb 26 19:36:35 2025 +0700
+++ b/mod_firewall/conditions.lib.lua Mon Mar 17 23:42:11 2025 +0700
@@ -123,7 +123,7 @@
end
function condition_handlers.SUBSCRIBED()
- return "(bare_to == bare_from or to_node and rostermanager.is_contact_subscribed(to_node, to_host, bare_from))",
+ return "(bare_to == bare_from or to_node and rostermanager.is_user_subscribed(to_node, to_host, bare_from))",
{ "rostermanager", "split_to", "bare_to", "bare_from" };
end
diff -r 805515dd2960 -r e20901443eae mod_groups_internal/mod_groups_internal.lua
--- a/mod_groups_internal/mod_groups_internal.lua Wed Feb 26 19:36:35 2025 +0700
+++ b/mod_groups_internal/mod_groups_internal.lua Mon Mar 17 23:42:11 2025 +0700
@@ -11,7 +11,7 @@
local group_members_store = module:open_store("groups");
local group_memberships = module:open_store("groups", "map");
-local muc_host_name = module:get_option("groups_muc_host", "groups."..host);
+local muc_host_name = module:get_option("groups_muc_host");
local muc_host = nil;
local is_contact_subscribed = rostermanager.is_contact_subscribed;
@@ -31,6 +31,7 @@
if group_name then
local user_roster = rostermanager.load_roster(user, host);
user_roster[contact_jid].groups[group_name] = true;
+ rostermanager.save_roster(user, host, user_roster, contact_jid);
end
-- Push updates to both rosters
@@ -207,10 +208,18 @@
function delete(group_id)
if group_members_store:set(group_id, nil) then
local group_info = get_info(group_id);
- if group_info and group_info.muc_jid then
- local room = muc_host.get_room_from_jid(group_info.muc_jid)
- if room then
- room:destroy()
+ if group_info then
+ if group_info.muc_jid then
+ local room = muc_host.get_room_from_jid(group_info.muc_jid)
+ if room then
+ room:destroy()
+ end
+ end
+ for _, muc_jid in ipairs(group_info.mucs) do
+ local room = muc_host.get_room_from_jid(muc_jid)
+ if room then
+ room:destroy()
+ end
end
end
return group_info_store:set(group_id, nil);
diff -r 805515dd2960 -r e20901443eae mod_http_index/mod_http_index.lua
--- a/mod_http_index/mod_http_index.lua Wed Feb 26 19:36:35 2025 +0700
+++ b/mod_http_index/mod_http_index.lua Mon Mar 17 23:42:11 2025 +0700
@@ -7,8 +7,8 @@
local base_template;
do
- local template_file = module:get_option_string(module.name .. "_template", module.name .. ".html");
- template_file = assert(module:load_resource(template_file));
+ local template_file = module:get_option_path(module.name .. "_template", "html/" .. module.name .. ".html");
+ template_file = assert(io.open(template_file));
base_template = template_file:read("*a");
template_file:close();
end
diff -r 805515dd2960 -r e20901443eae mod_http_oauth2/html/consent.html
--- a/mod_http_oauth2/html/consent.html Wed Feb 26 19:36:35 2025 +0700
+++ b/mod_http_oauth2/html/consent.html Mon Mar 17 23:42:11 2025 +0700
@@ -45,7 +45,7 @@
select 'Allow'. Otherwise, select 'Deny'.
-
+
diff -r 805515dd2960 -r e20901443eae mod_http_oauth2/mod_http_oauth2.lua
--- a/mod_http_oauth2/mod_http_oauth2.lua Wed Feb 26 19:36:35 2025 +0700
+++ b/mod_http_oauth2/mod_http_oauth2.lua Mon Mar 17 23:42:11 2025 +0700
@@ -28,6 +28,27 @@
end
end
+local function strict_url_parse(urlstr)
+ local url_parts = url.parse(urlstr);
+ if not url_parts then return url_parts; end
+ if url_parts.userinfo then return false; end
+ if url_parts.port then
+ local port = tonumber(url_parts.port);
+ if not port then return false; end
+ if port <= 0 or port > 0xffff then return false; end
+ if port ~= math.floor(port) then return false; end
+ end
+ if url_parts.host then
+ if encodings.stringprep.nameprep(url_parts.host) ~= url_parts.host then
+ return false;
+ end
+ if not encodings.idna.to_ascii(url_parts.host) then
+ return false;
+ end
+ end
+ return url_parts;
+end
+
local function strict_formdecode(query)
if not query then
return nil;
@@ -697,8 +718,13 @@
if not request.headers.authorization then return; end
local auth_type, auth_data = string.match(request.headers.authorization, "^(%S+)%s(.+)$");
+ if not auth_type then return nil; end
- if auth_type == "Basic" then
+ -- As described in Section 2.3 of [RFC5234], the string Bearer is case-insensitive.
+ -- https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-11#section-5.1.1
+ auth_type = auth_type:lower();
+
+ if auth_type == "basic" then
local creds = base64.decode(auth_data);
if not creds then return; end
local username, password = string.match(creds, "^([^:]+):(.*)$");
@@ -708,7 +734,7 @@
username = username;
password = password;
};
- elseif auth_type == "Bearer" then
+ elseif auth_type == "bearer" then
return {
type = "bearer";
bearer_token = auth_data;
@@ -1356,7 +1382,7 @@
end
local function redirect_uri_allowed(redirect_uri, client_uri, app_type)
- local uri = url.parse(redirect_uri);
+ local uri = strict_url_parse(redirect_uri);
if not uri then
return false;
end
@@ -1391,8 +1417,8 @@
});
end
- local client_uri = url.parse(client_metadata.client_uri);
- if not client_uri or client_uri.scheme ~= "https" or loopbacks:contains(client_uri.host) then
+ local client_uri = strict_url_parse(client_metadata.client_uri);
+ if not client_uri or client_uri.scheme ~= "https" or not client_uri.host or loopbacks:contains(client_uri.host) then
return nil, oauth_error("invalid_client_metadata", "Missing, invalid or insecure client_uri");
end
@@ -1558,6 +1584,7 @@
-- This is the normal 'authorization_code' flow.
-- Step 1. Create OAuth client
+ ["GET /register"] = { headers = { content_type = "application/schema+json" }; body = json.encode(registration_schema) };
["POST /register"] = handle_register_request;
-- Device flow
@@ -1569,24 +1596,6 @@
["POST /authorize"] = handle_authorization_request;
["OPTIONS /authorize"] = { status_code = 403; body = "" };
- -- Step 3. User is redirected to the 'redirect_uri' along with an
- -- authorization code. In the insecure 'implicit' flow, the access token
- -- is delivered here.
-
- -- Step 4. Retrieve access token using the code.
- ["POST /token"] = handle_token_grant;
-
- -- Step 4 is later repeated using the refresh token to get new access tokens.
-
- -- Step 5. Revoke token (access or refresh)
- ["POST /revoke"] = handle_revocation_request;
-
- -- Get info about a token
- ["POST /introspect"] = handle_introspection_request;
-
- -- OpenID
- ["GET /userinfo"] = handle_userinfo_request;
-
-- Optional static content for templates
["GET /style.css"] = templates.css and {
headers = {
@@ -1601,11 +1610,26 @@
body = templates.js;
} or nil;
- -- Some convenient fallback handlers
- ["GET /register"] = { headers = { content_type = "application/schema+json" }; body = json.encode(registration_schema) };
+ -- Step 3. User is redirected to the 'redirect_uri' along with an
+ -- authorization code. In the insecure 'implicit' flow, the access token
+ -- is delivered here.
+
+ -- Step 4. Retrieve access token using the code.
+ ["POST /token"] = handle_token_grant;
["GET /token"] = function() return 405; end;
+
+ -- Step 4 is later repeated using the refresh token to get new access tokens.
+
+ -- Get info about a token
+ ["POST /introspect"] = handle_introspection_request;
+ ["GET /introspect"] = function() return 405; end;
+
+ -- Get info about the user, used for OpenID Connect
+ ["GET /userinfo"] = handle_userinfo_request;
+
+ -- Step 5. Revoke token (access or refresh)
+ ["POST /revoke"] = handle_revocation_request;
["GET /revoke"] = function() return 405; end;
- ["GET /introspect"] = function() return 405; end;
};
});
@@ -1652,7 +1676,7 @@
ui_locales_supported = allowed_locales[1] and allowed_locales;
-- OpenID
- userinfo_endpoint = handle_register_request and module:http_url() .. "/userinfo" or nil;
+ userinfo_endpoint = handle_userinfo_request and module:http_url() .. "/userinfo" or nil;
jwks_uri = nil; -- REQUIRED in OpenID Discovery but not in OAuth 2.0 Metadata
id_token_signing_alg_values_supported = { "HS256" }; -- The algorithm RS256 MUST be included, but we use HS256 and client_secret as shared key.
}
diff -r 805515dd2960 -r e20901443eae mod_invites/mod_invites.lua
--- a/mod_invites/mod_invites.lua Wed Feb 26 19:36:35 2025 +0700
+++ b/mod_invites/mod_invites.lua Mon Mar 17 23:42:11 2025 +0700
@@ -191,7 +191,7 @@
type = token_info and token_info.type or "roster";
uri = token_info and token_info.uri or get_uri("roster", username.."@"..module.host, token);
additional_data = token_info and token_info.additional_data or nil;
- reusable = token_info.reusable;
+ reusable = token_info and token_info.reusable or false;
}, valid_invite_mt);
end
diff -r 805515dd2960 -r e20901443eae mod_lastlog2/mod_lastlog2.lua
--- a/mod_lastlog2/mod_lastlog2.lua Wed Feb 26 19:36:35 2025 +0700
+++ b/mod_lastlog2/mod_lastlog2.lua Mon Mar 17 23:42:11 2025 +0700
@@ -47,7 +47,7 @@
end);
end
-do
+if module.host ~= "*" then
local user_sessions = prosody.hosts[module.host].sessions;
local kv_store = module:open_store();
function get_last_active(username) --luacheck: ignore 131/get_last_active
@@ -67,6 +67,31 @@
end
end
+module:add_item("shell-command", {
+ section = "lastlog";
+ section_desc = "View and manage user activity data";
+ name = "show";
+ desc = "View recorded user activity for user";
+ args = { { name = "jid"; type = "string" } };
+ host_selector = "jid";
+ handler = function(self, userjid)
+ local kv_store = module:open_store();
+ local username = jid.prepped_split(userjid);
+ local lastlog, err = kv_store:get(username);
+ if err then return false, err; end
+ if not lastlog then return true, "No record found"; end
+ local print = self.session.print;
+ for event, data in pairs(lastlog) do
+ print(("Last %s: %s"):format(event,
+ data.timestamp and os.date("%Y-%m-%d %H:%M:%S", data.timestamp) or ""));
+ if data.ip then
+ print("IP address: "..data.ip);
+ end
+ end
+ return true, "Record shown"
+ end;
+});
+
function module.command(arg)
if not arg[1] or arg[1] == "--help" then
require"util.prosodyctl".show_usage([[mod_lastlog2 ]], [[Show when user last logged in or out]]);
diff -r 805515dd2960 -r e20901443eae mod_log_json/mod_log_json.lua
--- a/mod_log_json/mod_log_json.lua Wed Feb 26 19:36:35 2025 +0700
+++ b/mod_log_json/mod_log_json.lua Mon Mar 17 23:42:11 2025 +0700
@@ -9,7 +9,12 @@
local function sink_maker(config)
local send = function () end
if config.filename then
- local logfile = io.open(config.filename, "a+");
+ local logfile;
+ if config.filename == "/dev/stdout" then
+ logfile = io.stdout;
+ else
+ logfile = io.open(config.filename, "a+");
+ end
logfile:setvbuf("no");
function send(payload)
logfile:write(payload, "\n");
diff -r 805515dd2960 -r e20901443eae mod_measure_message_e2ee/mod_measure_message_e2ee.lua
--- a/mod_measure_message_e2ee/mod_measure_message_e2ee.lua Wed Feb 26 19:36:35 2025 +0700
+++ b/mod_measure_message_e2ee/mod_measure_message_e2ee.lua Mon Mar 17 23:42:11 2025 +0700
@@ -3,6 +3,7 @@
local count_openpgp = module:measure("openpgp", "rate");
local count_otr = module:measure("otr", "rate");
local count_ox = module:measure("ox", "rate");
+local count_omemo2 = module:measure("omemo2", "rate");
local count_omemo = module:measure("omemo", "rate");
local count_encrypted = module:measure("encrypted", "rate");
@@ -22,6 +23,11 @@
return;
end
+ if stanza:get_child("encrypted", "urn:xmpp:omemo:2") then
+ count_omemo2();
+ return;
+ end
+
if stanza:get_child("encrypted", "eu.siacs.conversations.axolotl") then
count_omemo();
return;