Software / code / prosody
File
plugins/mod_account_activity.lua @ 13792:4ea7bd7325be 13.0
core.portmanager: Restore use of per-host 'ssl' for SNI hosts. Fixes #1915.
This was an unintentional regression, as per-host 'ssl' options became valid
in 0.12 when SNI support was added for direct TLS ports. While we encourage
most people to use the simpler automatic certificate selection (and it seems
most do, given the overlooking of this bug), there are likely always going to
be use cases for manually-configured certificates.
The issue was introduced in commit 7e9ebdc75ce4 which inadvertently removed
the per-host option checking for SNI.
| author | Kim Alvefur <zash@zash.se> |
|---|---|
| date | Sat, 29 Mar 2025 22:25:19 +0100 |
| parent | 13682:0055c177a54c |
line wrap: on
line source
local jid = require "prosody.util.jid"; local time = os.time; local store = module:open_store(nil, "keyval+"); module:hook("authentication-success", function(event) local session = event.session; if session.username then store:set_key(session.username, "timestamp", time()); end end); module:hook("resource-unbind", function(event) local session = event.session; if session.username then store:set_key(session.username, "timestamp", time()); end end); local user_sessions = prosody.hosts[module.host].sessions; function get_last_active(username) --luacheck: ignore 131/get_last_active if user_sessions[username] then return os.time(), true; -- Currently connected else local last_activity = store:get(username); if not last_activity then return nil; end return last_activity.timestamp; end end module:add_item("shell-command", { section = "user"; section_desc = "View user activity data"; name = "activity"; desc = "View the last recorded user activity for an account"; args = { { name = "jid"; type = "string" } }; host_selector = "jid"; handler = function(self, userjid) --luacheck: ignore 212/self local username = jid.prepped_split(userjid); local last_timestamp, is_online = get_last_active(username); if not last_timestamp then return true, "No activity"; end return true, ("%s (%s)"):format(os.date("%Y-%m-%d %H:%M:%S", last_timestamp), (is_online and "online" or "offline")); end; }); module:add_item("shell-command", { section = "user"; section_desc = "View user activity data"; name = "list_inactive"; desc = "List inactive user accounts"; args = { { name = "host"; type = "string" }; { name = "duration"; type = "string" }; }; host_selector = "host"; handler = function(self, host, duration) --luacheck: ignore 212/self local um = require "prosody.core.usermanager"; local duration_sec = require "prosody.util.human.io".parse_duration(duration or ""); if not duration_sec then return false, ("Invalid duration %q - try something like \"30d\""):format(duration); end local now = os.time(); local n_inactive, n_unknown = 0, 0; for username in um.users(host) do local last_active = store:get_key(username, "timestamp"); if not last_active then local created_at = um.get_account_info(username, host).created; if created_at and (now - created_at) > duration_sec then self.session.print(username, ""); n_inactive = n_inactive + 1; elseif not created_at then n_unknown = n_unknown + 1; end elseif (now - last_active) > duration_sec then self.session.print(username, os.date("%Y-%m-%dT%T", last_active)); n_inactive = n_inactive + 1; end end if n_unknown > 0 then return true, ("%d accounts inactive since %s (%d unknown)"):format(n_inactive, os.date("%Y-%m-%dT%T", now - duration_sec), n_unknown); end return true, ("%d accounts inactive since %s"):format(n_inactive, os.date("%Y-%m-%dT%T", now - duration_sec)); end; }); module:add_item("shell-command", { section = "migrate"; section_desc = "Perform data migrations"; name = "account_activity_lastlog2"; desc = "Migrate account activity information from mod_lastlog2"; args = { { name = "host"; type = "string" } }; host_selector = "host"; handler = function(self, host) --luacheck: ignore 212/host 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 n = n + 1; if n % 100 == 0 then self.session.print(("Processed %d..."):format(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 self.session.print(("WW: Failed to import %q: %s"):format(username, err)); n_errors = n_errors + 1; end else n_skipped = n_skipped + 1; end 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; });