Mercurial > prosody-modules
view mod_invites_register_web/mod_invites_register_web.lua @ 6082:1a6cd0bbb7ab
mod_compliance_2023: Add 2023 Version of the compliance module, basis is the 2021 Version.
diff --git a/mod_compliance_2023/README.md b/mod_compliance_2023/README.md
new file mode 100644
--- /dev/null
+++ b/mod_compliance_2023/README.md
@@ -0,0 +1,22 @@
+---
+summary: XMPP Compliance Suites 2023 self-test
+labels:
+- Stage-Beta
+rockspec:
+ dependencies:
+ - mod_cloud_notify
+
+...
+
+Compare the list of enabled modules with
+[XEP-0479: XMPP Compliance Suites 2023] and produce basic report to the
+Prosody log file.
+
+If installed with the Prosody plugin installer then all modules needed for a green checkmark should be included. (With prosody 0.12 only [mod_cloud_notify] is not included with prosody and we need the community module)
+
+# Compatibility
+
+ Prosody-Version Status
+ --------------- ----------------------
+ trunk Works as of 2024-12-21
+ 0.12 Works
diff --git a/mod_compliance_2023/mod_compliance_2023.lua b/mod_compliance_2023/mod_compliance_2023.lua
new file mode 100644
--- /dev/null
+++ b/mod_compliance_2023/mod_compliance_2023.lua
@@ -0,0 +1,79 @@
+-- Copyright (c) 2021 Kim Alvefur
+--
+-- This module is MIT licensed.
+
+local hostmanager = require "core.hostmanager";
+
+local array = require "util.array";
+local set = require "util.set";
+
+local modules_enabled = module:get_option_inherited_set("modules_enabled");
+
+for host in pairs(hostmanager.get_children(module.host)) do
+ local component = module:context(host):get_option_string("component_module");
+ if component then
+ modules_enabled:add(component);
+ modules_enabled:include(module:context(host):get_option_set("modules_enabled", {}));
+ end
+end
+
+local function check(suggested, alternate, ...)
+ if set.intersection(modules_enabled, set.new({suggested; alternate; ...})):empty() then return suggested; end
+ return false;
+end
+
+local compliance = {
+ array {"Server"; check("tls"); check("disco")};
+
+ array {"Advanced Server"; check("pep", "pep_simple")};
+
+ array {"Web"; check("bosh"); check("websocket")};
+
+ -- No Server requirements for Advanced Web
+
+ array {"IM"; check("vcard_legacy", "vcard"); check("carbons"); check("http_file_share", "http_upload")};
+
+ array {
+ "Advanced IM";
+ check("vcard_legacy", "vcard");
+ check("blocklist");
+ check("muc");
+ check("private");
+ check("smacks");
+ check("mam");
+ check("bookmarks");
+ };
+
+ array {"Mobile"; check("smacks"); check("csi_simple", "csi_battery_saver")};
+
+ array {"Advanced Mobile"; check("cloud_notify")};
+
+ array {"A/V Calling"; check("turn_external", "external_services", "turncredentials", "extdisco")};
+
+};
+
+function check_compliance()
+ local compliant = true;
+ for _, suite in ipairs(compliance) do
+ local section = suite:pop(1);
+ if module:get_option_boolean("compliance_" .. section:lower():gsub("%A", "_"), true) then
+ local missing = set.new(suite:filter(function(m) return type(m) == "string" end):map(function(m) return "mod_" .. m end));
+ if suite[1] then
+ if compliant then
+ compliant = false;
+ module:log("warn", "Missing some modules for XMPP Compliance 2023");
+ end
+ module:log("info", "%s Compliance: %s", section, missing);
+ end
+ end
+ end
+
+ if compliant then module:log("info", "XMPP Compliance 2023: Compliant ✔️"); end
+end
+
+if prosody.start_time then
+ check_compliance()
+else
+ module:hook_global("server-started", check_compliance);
+end
+
| author | Menel <menel@snikket.de> |
|---|---|
| date | Sun, 22 Dec 2024 16:06:28 +0100 |
| parents | 15cf32e666da |
| children | 29a646bad096 |
line wrap: on
line source
local id = require "util.id"; local http_formdecode = require "net.http".formdecode; local usermanager = require "core.usermanager"; local modulemanager = require "core.modulemanager"; local nodeprep = require "util.encodings".stringprep.nodeprep; local st = require "util.stanza"; local url_escape = require "util.http".urlencode; local render_html_template = require"util.interpolation".new("%b{}", st.xml_escape, { urlescape = url_escape; }); local render_url = require "util.interpolation".new("%b{}", url_escape, { urlescape = url_escape; noscheme = function (url) return (url:gsub("^[^:]+:", "")); end; }); module:depends("register_apps"); local mod_password_policy = module:depends("password_policy"); local site_name = module:get_option_string("site_name", module.host); local site_apps = module:shared("register_apps/apps"); local webchat_url = module:get_option_string("webchat_url"); -- If not provided, but mod_conversejs is loaded, default to that if not webchat_url and modulemanager.get_modules_for_host(module.host):contains("conversejs") then local conversejs = module:depends("conversejs"); webchat_url = conversejs.module:http_url(); end module:depends("http"); local invites = module:depends("invites"); local invites_page = module:depends("invites_page"); function serve_register_page(event) local register_page_template = assert(module:load_resource("html/register.html")):read("*a"); local query_params = event.request.url.query and http_formdecode(event.request.url.query); local invite = query_params and invites.get(query_params.t); if not invite then return { status_code = 303; headers = { ["Location"] = invites_page.module:http_url().."?"..(event.request.url.query or ""); }; }; end event.response.headers["Content-Type"] = "text/html; charset=utf-8"; local invite_page = render_html_template(register_page_template, { site_name = site_name; token = invite.token; domain = module.host; uri = invite.uri; type = invite.type; jid = invite.jid; inviter = invite.inviter; app = query_params.c and site_apps[query_params.c]; password_policy = mod_password_policy.get_policy(); }); return invite_page; end function handle_register_form(event) local request, response = event.request, event.response; local form_data = http_formdecode(request.body); local user, password, token = form_data["user"], form_data["password"], form_data["token"]; local app_id = form_data["app_id"]; local register_page_template = assert(module:load_resource("html/register.html")):read("*a"); local error_template = assert(module:load_resource("html/register_error.html")):read("*a"); local invite = invites.get(token); if not invite then return { status_code = 303; headers = { ["Location"] = invites_page.module:http_url().."?"..event.request.url.query; }; }; end event.response.headers["Content-Type"] = "text/html; charset=utf-8"; if not user or #user == 0 or not password or #password == 0 or not token then return render_html_template(register_page_template, { site_name = site_name; token = invite.token; domain = module.host; uri = invite.uri; type = invite.type; jid = invite.jid; password_policy = mod_password_policy.get_policy(); msg_class = "alert-warning"; message = "Please fill in all fields."; }); end -- Shamelessly copied from mod_register_web. local prepped_username = nodeprep(user); if not prepped_username or #prepped_username == 0 then return render_html_template(register_page_template, { site_name = site_name; token = invite.token; domain = module.host; uri = invite.uri; type = invite.type; jid = invite.jid; password_policy = mod_password_policy.get_policy(); msg_class = "alert-warning"; message = "This username contains invalid characters."; }); end if usermanager.user_exists(prepped_username, module.host) then return render_html_template(register_page_template, { site_name = site_name; token = invite.token; domain = module.host; uri = invite.uri; type = invite.type; jid = invite.jid; password_policy = mod_password_policy.get_policy(); msg_class = "alert-warning"; message = "This username is already in use."; }); end local pw_ok, pw_error = mod_password_policy.check_password(password, { username = prepped_username; }); if not pw_ok then return render_html_template(register_page_template, { site_name = site_name; token = invite.token; domain = module.host; uri = invite.uri; type = invite.type; jid = invite.jid; password_policy = mod_password_policy.get_policy(); msg_class = "alert-warning"; message = pw_error; }); end local registering = { validated_invite = invite; username = prepped_username; host = module.host; ip = request.ip; allowed = true; }; module:fire_event("user-registering", registering); if not registering.allowed then return render_html_template(error_template, { site_name = site_name; msg_class = "alert-danger"; message = registering.reason or "Registration is not allowed."; }); end local ok, err = usermanager.create_user(prepped_username, password, module.host); if ok then module:fire_event("user-registered", { username = prepped_username; host = module.host; source = "mod_"..module.name; validated_invite = invite; ip = request.ip; }); local app_info = site_apps[app_id]; local success_template; if app_info then if app_info.login_link_format then local redirect_url = render_url(app_info.login_link_format, { site_name = site_name; username = prepped_username; domain = module.host; password = password; app = app_info; }); return { status_code = 303; headers = { ["Location"] = redirect_url; }; }; end -- If recognised app, we serve a page that includes setup instructions success_template = assert(module:load_resource("html/register_success_setup.html")):read("*a"); else success_template = assert(module:load_resource("html/register_success.html")):read("*a"); end -- Due to the credentials being served here, ensure that -- the browser or any intermediary does not cache the page event.response.headers["Cache-Control"] = "no-cache, no-store, must-revalidate"; event.response.headers["Pragma"] = "no-cache"; event.response.headers["Expires"] = "0"; return render_html_template(success_template, { site_name = site_name; username = prepped_username; domain = module.host; password = password; app = app_info; webchat_url = webchat_url; }); else local err_id = id.short(); module:log("warn", "Registration failed (%s): %s", err_id, tostring(err)); return render_html_template(error_template, { site_name = site_name; msg_class = "alert-danger"; message = ("An unknown error has occurred (%s)"):format(err_id); }); end end module:provides("http", { default_path = "register"; route = { ["GET"] = serve_register_page; ["POST"] = handle_register_form; }; });
