Changeset

6273:8ceedc336d0d

Merge update
author Trần H. Trung <xmpp:trần.h.trung@trung.fun>
date Sun, 01 Jun 2025 13:51:38 +0700
parents 6263:10a1016d1c3a (current diff) 6272:ed6fa901cf94 (diff)
children 6274:6cf1f47f24b4
files mod_http_oauth2/mod_http_oauth2.lua mod_invites_register_web/README.md mod_invites_register_web/mod_invites_register_web.lua
diffstat 8 files changed, 152 insertions(+), 59 deletions(-) [+]
line wrap: on
line diff
--- a/mod_http_oauth2/mod_http_oauth2.lua	Sun Jun 01 11:43:16 2025 +0700
+++ b/mod_http_oauth2/mod_http_oauth2.lua	Sun Jun 01 13:51:38 2025 +0700
@@ -399,32 +399,19 @@
 end
 
 local function make_client_secret(client_id) --> client_secret
-       return hashes.hmac_sha256(verification_key, client_id, true);
+	return hashes.hmac_sha256(verification_key, client_id, true);
 end
 
 local function verify_client_secret(client_id, client_secret)
-       return hashes.equals(make_client_secret(client_id), client_secret);
+	return hashes.equals(make_client_secret(client_id), client_secret);
 end
 
-function grant_type_handlers.password(params)
-	if not params.client_id then return oauth_error("invalid_request", "missing 'client_id'"); end
-	if not params.client_secret then return oauth_error("invalid_request", "missing 'client_secret'"); end
-
-	local client = check_client(params.client_id);
-	if not client then
-		return oauth_error("invalid_client", "incorrect credentials");
-	end
-
-	if not verify_client_secret(params.client_id, params.client_secret) then
-		module:log("debug", "client_secret mismatch");
-		return oauth_error("invalid_client", "incorrect credentials");
-	end
-
+function grant_type_handlers.password(params, client)
 	local request_username
 
 	if expect_username_jid then
 		local request_jid = assert(params.username, oauth_error("invalid_request", "missing 'username' (JID)"));
-		local _request_username, request_host, request_resource = jid.prepped_split(request_jid);
+		local _request_username, request_host = jid.prepped_split(request_jid);
 
 		if not (_request_username and request_host) or request_host ~= module.host then
 			return oauth_error("invalid_request", "invalid JID");
@@ -537,24 +524,12 @@
 	}
 end
 
-function grant_type_handlers.authorization_code(params)
-	if not params.client_id then return oauth_error("invalid_request", "missing 'client_id'"); end
-	if not params.client_secret then return oauth_error("invalid_request", "missing 'client_secret'"); end
+function grant_type_handlers.authorization_code(params, client)
 	if not params.code then return oauth_error("invalid_request", "missing 'code'"); end
 	if params.scope and params.scope ~= "" then
 		-- FIXME allow a subset of granted scopes
 		return oauth_error("invalid_scope", "unknown scope requested");
 	end
-
-	local client = check_client(params.client_id);
-	if not client then
-		return oauth_error("invalid_client", "incorrect credentials");
-	end
-
-	if not verify_client_secret(params.client_id, params.client_secret) then
-		module:log("debug", "client_secret mismatch");
-		return oauth_error("invalid_client", "incorrect credentials");
-	end
 	local code, err = codes:get("authorization_code:" .. params.client_id .. "#" .. params.code);
 	if err then error(err); end
 	-- MUST NOT use the authorization code more than once, so remove it to
@@ -884,12 +859,26 @@
 		params.client_secret = http.urldecode(credentials.password);
 	end
 
+	if not params.client_id then return oauth_error("invalid_request", "missing 'client_id'"); end
+	if not params.client_secret then return oauth_error("invalid_request", "missing 'client_secret'"); end
+
+	local client = check_client(params.client_id);
+	if not client then
+		return oauth_error("invalid_client", "incorrect credentials");
+	end
+
+	if not verify_client_secret(params.client_id, params.client_secret) then
+		module:log("debug", "client_secret mismatch");
+		return oauth_error("invalid_client", "incorrect credentials");
+	end
+
+
 	local grant_type = params.grant_type
 	local grant_handler = grant_type_handlers[grant_type];
 	if not grant_handler then
 		return oauth_error("invalid_request", "No such grant type.");
 	end
-	return grant_handler(params);
+	return grant_handler(params, client);
 end
 
 local function handle_authorization_request(event)
--- a/mod_invites_register_web/README.md	Sun Jun 01 11:43:16 2025 +0700
+++ b/mod_invites_register_web/README.md	Sun Jun 01 13:51:38 2025 +0700
@@ -34,8 +34,8 @@
 validates invite tokens. It also supports guiding the user through client
 download and configuration via mod_register_apps.
 
-This module depends on mod_invites_page solely for the case where an invalid
-invite token is received - it will redirect to mod_invites_page so that an
+This module depends on [mod_invites_page] solely for the case where an invalid
+invite token is received - it will redirect to [mod_invites_page] so that an
 appropriate error can be served to the user.
 
 The module also depends on [mod_password_policy] (which will be automatically
@@ -46,36 +46,43 @@
 Configuration
 =============
 
-It uses the optional `site_name` to override the displayed site name.
+The optional `site_name` setting can be used to override the displayed site name.
+
+```lua
+site_name = "My Chat Service"
+```
 
 You can set `webchat_url` to the URL of a web chat that will be linked
-to after successful registration. If not specified but mod_conversejs is loaded
+to after successful registration. If not specified but [mod_conversejs] is loaded
 on the current host, it will default to the URL of that module.
 
-You can use your own html templates with `invites_template_html`. Names of the
-files MUST match the default. More over, you can offer multiple (human)
+HTML templates can be overridden by using `invites_register_template_path`, see
+the `html/` directory in the sources for the files needed.
+
+```lua
+invites_register_template_path = "/path/to/templates/html"
+```
+
+Names of the files MUST match the default. More over, you can offer multiple (human)
 languages by adding the `&l=` to the URL. Meaning this module will serve
 `register.html` for your default URL:
-```
 
-    https://prosody.example.net/register?t=aowiefjoaij
-
+```lua
+https://prosody.example.net/register?t=aowiefjoaij
 ``` 
 
 And if you have a `register.en.html` in the directory you have specified in
 your config file, it will be served at:
-```
 
-    https://prosody.example.net/register?t=aowiefjoaij&l=en
-
+```lua
+https://prosody.example.net/register?t=aowiefjoaij&l=en
 ```
 
 So in your `register.html`, you can point to the English version by using an
 `<a>` tag like this:
-```
 
-    <a href="/register?t={token}&l=en">English</a>
-
+```lua
+<a href="/register?t={token}&l=en">English</a>
 ```
 
 You can further customize your URL with [mod_invites_page] too.
--- a/mod_invites_register_web/mod_invites_register_web.lua	Sun Jun 01 11:43:16 2025 +0700
+++ b/mod_invites_register_web/mod_invites_register_web.lua	Sun Jun 01 13:51:38 2025 +0700
@@ -34,10 +34,10 @@
 local invites = module:depends("invites");
 local invites_page = module:depends("invites_page");
 
-local templatePath = module:get_option_string("invites_template_html", "html");
-function template_get(sTemplate, sLang)
-	local template = sLang and templatePath.."/"..sTemplate.."."..sLang..".html" 
-		or templatePath.."/"..sTemplate..".html";
+local template_path = module:get_option_path("invites_register_template_path", "html");
+function template_get(template, lang)
+	local template = lang and template_path.."/"..template.."."..lang..".html" 
+		or template_path.."/"..template..".html";
 	return assert(module:load_resource(template):read("*a"));
 end
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mod_migrate_lastlog2/README.md	Sun Jun 01 13:51:38 2025 +0700
@@ -0,0 +1,21 @@
+---
+summary: mod_lastlog2 to mod_account_activity migrator
+labels:
+- Stage-Alpha
+---
+
+
+This is a migration script for converting data stored by [mod_lastlog2] (a
+community module) to mod_account_activity (a newer module which is supplied
+with Prosody 13.0 and later).
+
+# Usage
+
+This module performs the migration automatically as soon as it is loaded.
+
+By default it will remove data from the mod_lastlog2 store unless you set
+`migrate_lastlog2_auto_remove = false`.
+
+# Compatibility
+
+Works with Prosody 13.0 and later.
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/mod_migrate_lastlog2/mod_migrate_lastlog2.lua	Sun Jun 01 13:51:38 2025 +0700
@@ -0,0 +1,67 @@
+-- This module is based on the shell command code from mod_account_activity
+
+local autoremove = module:get_option_boolean("migrate_lastlog2_auto_remove", true);
+
+local function do_migration()
+	local store = module:open_store("account_activity", "keyval+");
+	local lastlog2 = module:open_store("lastlog2", "keyval+");
+	local n_updated, n_errors, n_skipped = 0, 0, 0;
+
+	local async = require "prosody.util.async";
+
+	local p = require "prosody.util.promise".new(function (resolve)
+		local async_runner = async.runner(function ()
+			local n = 0;
+			for username in lastlog2:items() do
+				local was_error = nil;
+				n = n + 1;
+				if n % 100 == 0 then
+					module:log("debug", "Processed %d...", n);
+					async.sleep(0);
+				end
+				local lastlog2_data = lastlog2:get(username);
+				if lastlog2_data then
+					local current_data, err = store:get(username);
+					if not current_data then
+						if not err then
+							current_data = {};
+						else
+							n_errors = n_errors + 1;
+						end
+					end
+					if current_data then
+						local imported_timestamp = current_data.timestamp;
+						local latest;
+						for k, v in pairs(lastlog2_data) do
+							if k ~= "registered" and (not latest or v.timestamp > latest) then
+								latest = v.timestamp;
+							end
+						end
+						if latest and (not imported_timestamp or imported_timestamp < latest) then
+							local ok, err = store:set_key(username, "timestamp", latest);
+							if ok then
+								n_updated = n_updated + 1;
+							else
+								module:log("error", "Failed to import %q: %s", username, err);
+								was_error = true;
+								n_errors = n_errors + 1;
+							end
+						else
+							n_skipped = n_skipped + 1;
+						end
+					end
+					if autoremove and not was_error then
+						lastlog2:set(username, nil);
+					end
+				end
+			end
+			return resolve(("%d accounts imported, %d errors, %d skipped"):format(n_updated, n_errors, n_skipped));
+		end);
+		async_runner:run(true);
+	end);
+	return p;
+end
+
+function module.ready()
+	do_migration();
+end
--- a/mod_push2/mod_push2.lua	Sun Jun 01 11:43:16 2025 +0700
+++ b/mod_push2/mod_push2.lua	Sun Jun 01 13:51:38 2025 +0700
@@ -355,6 +355,7 @@
 		end
 
 		if send_push then
+			local any_match = false;
 			local push_notification_payload = st.stanza("notification", { xmlns = xmlns_push })
 			push_notification_payload:text_tag("client", push_info.client)
 			push_notification_payload:text_tag("priority", is_voip(stanza) and "high" or (is_important(stanza, session) and "normal" or "low"))
@@ -411,6 +412,7 @@
 
 				if does_match and not sends_added[match.send] then
 					sends_added[match.send] = true
+					any_match = true
 					if match.send == "urn:xmpp:push2:send:notify-only" then
 						-- Nothing more to add
 					elseif match.send == "urn:xmpp:push2:send:sce+rfc8291+rfc8292:0" then
@@ -422,12 +424,14 @@
 				end
 			end
 
-			local push_publish = st.message({ to = push_info.service, from = module.host, id = uuid.generate() })
-				:add_child(push_notification_payload):up()
+			if any_match then
+				local push_publish = st.message({ to = push_info.service, from = module.host, id = uuid.generate() })
+					:add_child(push_notification_payload):up()
 
-			-- TODO: watch for message error replies and count or something
-			module:send(push_publish)
-			pushes = pushes + 1
+				-- TODO: watch for message error replies and count or something
+				module:send(push_publish)
+				pushes = pushes + 1
+			end
 		end
 	end
 
--- a/mod_rest/README.md	Sun Jun 01 11:43:16 2025 +0700
+++ b/mod_rest/README.md	Sun Jun 01 13:51:38 2025 +0700
@@ -37,13 +37,11 @@
 
 ## As a Component
 
-If you install this as a component, you won't be able to use user authentication above,
-and must use OAuth2 authentication outlined below.
+If you install this as a component, the HTTP Basic credentials are the components base JID along with its secret.
 
 ``` {.lua}
 Component "chat.example.com" "rest"
 component_secret = "dmVyeSBzZWNyZXQgdG9rZW4K"
-modules_enabled = {"http_oauth2"}
 ```
 
 ## User authentication
--- a/mod_rest/mod_rest.lua	Sun Jun 01 11:43:16 2025 +0700
+++ b/mod_rest/mod_rest.lua	Sun Jun 01 13:51:38 2025 +0700
@@ -64,7 +64,7 @@
 		return nil, post_errors.new("noauthz", { request = request });
 	end
 
-	if auth_type == "basic" then
+	if auth_type == "basic" and module:get_host_type() == "local" then
 		local creds = base64.decode(auth_data);
 		if not creds then
 			return nil, post_errors.new("malformauthz", { request = request });
@@ -81,6 +81,13 @@
 			return false, post_errors.new("unauthz", { request = request });
 		end
 		return { username = username; host = module.host };
+	elseif auth_type == "basic" and module:get_host_type() == "component" then
+		local component_secret = module:get_option_string("component_secret");
+		local creds = base64.decode(auth_data);
+		if creds ~= module.host .. ":" .. component_secret then
+			return nil, post_errors.new("malformauthz", { request = request });
+		end
+		return { host = module.host };
 	elseif auth_type == "bearer" then
 		if tokens.get_token_session then
 			local token_session, err = tokens.get_token_session(auth_data);