Mercurial > prosody-modules
changeset 6399:00735045109d
mod_invites: Remove, these are shipped with the oldest supported Prosody
| author | Kim Alvefur <zash@zash.se> |
|---|---|
| date | Sat, 07 Feb 2026 19:48:30 +0100 |
| parents | 9c9889ad4302 |
| children | f4efb8606b92 |
| files | mod_invites/README.md mod_invites/mod_invites.lua mod_invites_adhoc/README.md mod_invites_adhoc/mod_invites_adhoc.lua mod_invites_register/README.md mod_invites_register/mod_invites_register.lua |
| diffstat | 6 files changed, 9 insertions(+), 838 deletions(-) [+] |
line wrap: on
line diff
--- a/mod_invites/README.md Sat Feb 07 12:25:52 2026 +0100 +++ b/mod_invites/README.md Sat Feb 07 19:48:30 2026 +0100 @@ -2,94 +2,12 @@ labels: - 'Stage-Merged' summary: 'Invite management module for Prosody' +superseded_by: mod_invites ... Introduction ============ -::: {.alert .alert-info} -This module has been merged into Prosody as -[mod_invites][doc:modules:mod_invites]. Users of Prosody **0.12** -and later should not install this version. +::: {.alert .alert-error} +This module has been merged into Prosody as [mod_invites][doc:modules:mod_invites]. ::: - -This module is part of the suite of modules that implement invite-based -account registration for Prosody. The other modules are: - -- [mod_invites_adhoc][doc:modules:mod_invites_adhoc] -- [mod_invites_register][doc:modules:mod_invites_register] -- [mod_invites_page] -- [mod_invites_register_web] -- [mod_invites_api] -- [mod_register_apps] - -This module manages the creation and consumption of invite codes for the -host(s) it is loaded onto. It currently does not expose any admin/user-facing -functionality (though in the future it will probably gain a way to view/manage -pending invites). - -Instead, other modules can use the API from this module to create invite tokens -which can be used to e.g. register accounts or create automatic subscription -approvals. - -This module should not be confused with the similarly named mod_invite (note the -missing 's'!). That module was a precursor to this one that helped test and prove -the concept of invite-based registration, and is now deprecated. - -# Configuration - -This module exposes just one option - the length of time that a generated invite -should be valid for by default. - -``` {.lua} --- Configure the number of seconds a token is valid for (default 7 days) -invite_expiry = 86400 * 7 -``` - -# Invites setup - -For a fully-featured invite-based setup, the following provides an example -configuration: - -``` {.lua} --- Specify the external URL format of the invite links - -VirtualHost "example.com" - invites_page = "https://example.com/invite?{invite.token}" - http_external_url = "https://example.com/" - http_paths = { - invites_page = "/invite"; - invites_register_web = "/register"; - } - modules_enabled = { - "invites"; - "invites_adhoc"; - "invites_page"; - "invites_register"; - "invites_register_web"; - - "http_libjs"; -- See 'external dependencies' below - } -``` - -Restart Prosody and create a new invite using an ad-hoc command in an XMPP client connected -to your admin account, or use the command line: - - prosodyctl mod_invites generate example.com - -## External dependencies - -The default HTML templates for the web-based modules depend on some CSS and Javascript -libraries. They expect these to be available at `https://example.com/share`. An easy -way of doing this if you are on Debian 10 (buster) is to enable mod_http_libjs and install -the following packages: - - apt install libjs-bootstrap4 libjs-jquery - -On other systems you will need to manually put these libraries somewhere on the filesystem -that Prosody can read, and serve them using mod_http_libjs with a custom `libjs_path` -setting. - -# Compatibility - -0.11 and later.
--- a/mod_invites/mod_invites.lua Sat Feb 07 12:25:52 2026 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,339 +0,0 @@ -local id = require "util.id"; -local it = require "util.iterators"; -local url = require "socket.url"; -local jid_node = require "util.jid".node; -local jid_split = require "util.jid".split; - -local default_ttl = module:get_option_number("invite_expiry", 86400 * 7); - -local token_storage; -if prosody.process_type == "prosody" or prosody.shutdown then - token_storage = module:open_store("invite_token", "map"); -end - -local function get_uri(action, jid, token, params) --> string - return url.build({ - scheme = "xmpp", - path = jid, - query = action..";preauth="..token..(params and (";"..params) or ""), - }); -end - -local function create_invite(invite_action, invite_jid, allow_registration, additional_data, ttl, reusable) - local token = id.medium(); - - local created_at = os.time(); - local expires = created_at + (ttl or default_ttl); - - local invite_params = (invite_action == "roster" and allow_registration) and "ibr=y" or nil; - - local invite = { - type = invite_action; - jid = invite_jid; - - token = token; - allow_registration = allow_registration; - additional_data = additional_data; - - uri = get_uri(invite_action, invite_jid, token, invite_params); - - created_at = created_at; - expires = expires; - - reusable = reusable; - }; - - module:fire_event("invite-created", invite); - - if allow_registration then - local ok, err = token_storage:set(nil, token, invite); - if not ok then - module:log("warn", "Failed to store account invite: %s", err); - return nil, "internal-server-error"; - end - end - - if invite_action == "roster" then - local username = jid_node(invite_jid); - local ok, err = token_storage:set(username, token, expires); - if not ok then - module:log("warn", "Failed to store subscription invite: %s", err); - return nil, "internal-server-error"; - end - end - - return invite; -end - --- Create invitation to register an account (optionally restricted to the specified username) -function create_account(account_username, additional_data, ttl) --luacheck: ignore 131/create_account - local jid = account_username and (account_username.."@"..module.host) or module.host; - return create_invite("register", jid, true, additional_data, ttl); -end - --- Create invitation to reset the password for an account -function create_account_reset(account_username, ttl) --luacheck: ignore 131/create_account_reset - return create_account(account_username, { allow_reset = account_username }, ttl or 86400); -end - --- Create invitation to become a contact of a local user -function create_contact(username, allow_registration, additional_data, ttl) --luacheck: ignore 131/create_contact - return create_invite("roster", username.."@"..module.host, allow_registration, additional_data, ttl); -end - --- Create invitation to register an account and join a user group --- If explicit ttl is passed, invite is valid for multiple signups --- during that time period -function create_group(group_ids, additional_data, ttl) --luacheck: ignore 131/create_group - local merged_additional_data = { - groups = group_ids; - }; - if additional_data then - for k, v in pairs(additional_data) do - merged_additional_data[k] = v; - end - end - return create_invite("register", module.host, true, merged_additional_data, ttl, not not ttl); -end - --- Iterates pending (non-expired, unused) invites that allow registration -function pending_account_invites() --luacheck: ignore 131/pending_account_invites - local store = module:open_store("invite_token"); - local now = os.time(); - local function is_valid_invite(_, invite) - return invite.expires > now; - end - return it.filter(is_valid_invite, pairs(store:get(nil) or {})); -end - -function get_account_invite_info(token) --luacheck: ignore 131/get_account_invite_info - if not token then - return nil, "no-token"; - end - - -- Fetch from host store (account invite) - local token_info = token_storage:get(nil, token); - if not token_info then - return nil, "token-invalid"; - elseif os.time() > token_info.expires then - return nil, "token-expired"; - end - - return token_info; -end - -function delete_account_invite(token) --luacheck: ignore 131/delete_account_invite - if not token then - return nil, "no-token"; - end - - return token_storage:set(nil, token, nil); -end - -local valid_invite_methods = {}; -local valid_invite_mt = { __index = valid_invite_methods }; - -function valid_invite_methods:use() - if self.reusable then - return true; - end - - if self.username then - -- Also remove the contact invite if present, on the - -- assumption that they now have a mutual subscription - token_storage:set(self.username, self.token, nil); - end - token_storage:set(nil, self.token, nil); - - return true; -end - --- Get a validated invite (or nil, err). Must call :use() on the --- returned invite after it is actually successfully used --- For "roster" invites, the username of the local user (who issued --- the invite) must be passed. --- If no username is passed, but the registration is a roster invite --- from a local user, the "inviter" field of the returned invite will --- be set to their username. -function get(token, username) - if not token then - return nil, "no-token"; - end - - local valid_until, inviter; - - -- Fetch from host store (account invite) - local token_info = token_storage:get(nil, token); - - if username then -- token being used for subscription - -- Fetch from user store (subscription invite) - valid_until = token_storage:get(username, token); - else -- token being used for account creation - valid_until = token_info and token_info.expires; - if token_info and token_info.type == "roster" then - username = jid_node(token_info.jid); - inviter = username; - end - end - - if not valid_until then - module:log("debug", "Got unknown token: %s", token); - return nil, "token-invalid"; - elseif os.time() > valid_until then - module:log("debug", "Got expired token: %s", token); - return nil, "token-expired"; - end - - return setmetatable({ - token = token; - username = username; - inviter = inviter; - 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 and token_info.reusable or false; - }, valid_invite_mt); -end - -function use(token) --luacheck: ignore 131/use - local invite = get(token); - return invite and invite:use(); -end - ---- shell command -do - -- Since the console is global this overwrites the command for - -- each host it's loaded on, but this should be fine. - - local get_module = require "core.modulemanager".get_module; - - local console_env = module:shared("/*/admin_shell/env"); - - -- luacheck: ignore 212/self - console_env.invite = {}; - function console_env.invite:create_account(user_jid) - local username, host = jid_split(user_jid); - local mod_invites, err = get_module(host, "invites"); - if not mod_invites then return nil, err or "mod_invites not loaded on this host"; end - local invite, err = mod_invites.create_account(username); - if not invite then return nil, err; end - return true, invite.uri; - end - - function console_env.invite:create_contact(user_jid, allow_registration) - local username, host = jid_split(user_jid); - local mod_invites, err = get_module(host, "invites"); - if not mod_invites then return nil, err or "mod_invites not loaded on this host"; end - local invite, err = mod_invites.create_contact(username, allow_registration); - if not invite then return nil, err; end - return true, invite.uri; - end -end - ---- prosodyctl command -function module.command(arg) - if #arg < 2 or arg[1] ~= "generate" then - print("usage: prosodyctl mod_"..module.name.." generate example.com"); - return 2; - end - table.remove(arg, 1); -- pop command - - local sm = require "core.storagemanager"; - local mm = require "core.modulemanager"; - - local host = arg[1]; - assert(hosts[host], "Host "..tostring(host).." does not exist"); - sm.initialize_host(host); - table.remove(arg, 1); -- pop host - module.host = host; --luacheck: ignore 122/module - token_storage = module:open_store("invite_token", "map"); - - -- Load mod_invites - local invites = module:depends("invites"); - local invites_page_module = module:get_option_string("invites_page_module", "invites_page"); - if mm.get_modules_for_host(host):contains(invites_page_module) then - module:depends(invites_page_module); - end - - local allow_reset; - local roles; - local groups = {}; - - while #arg > 0 do - local value = arg[1]; - table.remove(arg, 1); - if value == "--help" then - print("usage: prosodyctl mod_"..module.name.." generate DOMAIN --reset USERNAME") - print("usage: prosodyctl mod_"..module.name.." generate DOMAIN [--admin] [--role ROLE] [--group GROUPID]...") - print() - print("This command has two modes: password reset and new account.") - print("If --reset is given, the command operates in password reset mode and in new account mode otherwise.") - print() - print("required arguments in password reset mode:") - print() - print(" --reset USERNAME Generate a password reset link for the given USERNAME.") - print() - print("optional arguments in new account mode:") - print() - print(" --admin Make the new user privileged") - print(" Equivalent to --role prosody:admin") - print(" --role ROLE Grant the given ROLE to the new user") - print(" --group GROUPID Add the user to the group with the given ID") - print(" Can be specified multiple times") - print() - print("--role and --admin override each other; the last one wins") - print("--group can be specified multiple times; the user will be added to all groups.") - print() - print("--reset and the other options cannot be mixed.") - return 2 - elseif value == "--reset" then - local nodeprep = require "util.encodings".stringprep.nodeprep; - local username = nodeprep(arg[1]) - table.remove(arg, 1); - if not username then - print("Please supply a valid username to generate a reset link for"); - return 2; - end - allow_reset = username; - elseif value == "--admin" then - roles = { ["prosody:admin"] = true }; - elseif value == "--role" then - local rolename = arg[1]; - if not rolename then - print("Please supply a role name"); - return 2; - end - roles = { [rolename] = true }; - table.remove(arg, 1); - elseif value == "--group" or value == "-g" then - local groupid = arg[1]; - if not groupid then - print("Please supply a group ID") - return 2; - end - table.insert(groups, groupid); - table.remove(arg, 1); - else - print("unexpected argument: "..value) - end - end - - local invite; - if allow_reset then - if roles then - print("--role/--admin and --reset are mutually exclusive") - return 2; - end - if #groups > 0 then - print("--group and --reset are mutually exclusive") - end - invite = assert(invites.create_account_reset(allow_reset)); - else - invite = assert(invites.create_account(nil, { - roles = roles, - groups = groups - })); - end - - print(invite.landing_page or invite.uri); -end
--- a/mod_invites_adhoc/README.md Sat Feb 07 12:25:52 2026 +0100 +++ b/mod_invites_adhoc/README.md Sat Feb 07 19:48:30 2026 +0100 @@ -2,56 +2,12 @@ labels: - 'Stage-Merged' summary: 'Enable ad-hoc command for XMPP clients to create invitations' +superseded_by: mod_invites_adhoc ... Introduction ============ -::: {.alert .alert-info} -This module has been merged into Prosody as -[mod_invites_adhoc][doc:modules:mod_invites_adhoc]. Users of Prosody **0.12** -and later should not install this version. +::: {.alert .alert-error} +This module has been merged into Prosody as [mod_invites_adhoc][doc:modules:mod_invites_adhoc]. ::: - -This module is part of the suite of modules that implement invite-based -account registration for Prosody. The other modules are: - -- [mod_invites][doc:modules:mod_invites] -- [mod_invites_register][doc:modules:mod_invites_register] -- [mod_invites_page] -- [mod_invites_register_web] -- [mod_invites_api] -- [mod_register_apps] - -For details and a full overview, start with the [mod_invites] documentation. - -Details -======= - -mod_invites_adhoc allows XMPP clients to create new invites on the server. -Clients must support either XEP-0401 (Easy Onboarding) or XEP-0050 (Ad-hoc -commands). - -There are three types of invitation that can be created: - -| Invite type | Description | -|--|--| -| Account-only invites | These can be used to register a new account | -| Contact-only invites | These can be shared with a contact so they can easily add you to their contact list | -| Account-and-contact invites | Like a contact-only invite, but also allows the contact to register on the current server if they don't already have an XMPP account | - -Only configured admins of the server are able to create account-only invites. By default -normal users may only create contact-only invites, but account-and-contact invites can -be enabled with the `allow_user_invites` option. - -Configuration -============= - -| Name | Description | Default | -|-----------------------|-----------------------------------------------------------------------|-------------------------------------------| -| allow_user_invites | Whether non-admin users can invite contacts to register on this server| `false` | -| allow_contact_invites | Whether non-admin users can invite contacts to their roster | `true` | - -The `allow_user_invites` option should be set as desired. However it is -strongly recommended to leave the other option (`allow_contact_invites`) -at its default to provide the best user experience.
--- a/mod_invites_adhoc/mod_invites_adhoc.lua Sat Feb 07 12:25:52 2026 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,146 +0,0 @@ --- XEP-0401: Easy User Onboarding -local dataforms = require "util.dataforms"; -local datetime = require "util.datetime"; -local split_jid = require "util.jid".split; -local usermanager = require "core.usermanager"; - -local new_adhoc = module:require("adhoc").new; - --- Whether local users can invite other users to create an account on this server -local allow_user_invites = module:get_option_boolean("allow_user_invites", false); --- Who can see and use the contact invite command. It is strongly recommended to --- keep this available to all local users. To allow/disallow invite-registration --- on the server, use the option above instead. -local allow_contact_invites = module:get_option_boolean("allow_contact_invites", true); - --- These options are deprecated since module:may() -local allow_user_invite_roles = module:get_option_set("allow_user_invites_by_roles", {}); -local deny_user_invite_roles = module:get_option_set("deny_user_invites_by_roles", {}); - -if module.may then - if allow_user_invites then - if require "core.features".available:contains("split-user-roles") then - module:default_permission("prosody:registered", ":invite-new-users"); - else -- COMPAT - module:default_permission("prosody:user", ":invite-new-users"); - end - end - if not allow_user_invite_roles:empty() or not deny_user_invite_roles:empty() then - return error("allow_user_invites_by_roles and deny_user_invites_by_roles are deprecated options"); - end -end - -local invites; -if prosody.shutdown then -- COMPAT hack to detect prosodyctl - invites = module:depends("invites"); -end - -local invite_result_form = dataforms.new({ - title = "Your invite has been created", - { - name = "url" ; - var = "landing-url"; - label = "Invite web page"; - desc = "Share this link"; - }, - { - name = "uri"; - label = "Invite URI"; - desc = "This alternative link can be opened with some XMPP clients"; - }, - { - name = "expire"; - label = "Invite valid until"; - }, - }); - --- This is for checking if the specified JID may create invites --- that allow people to register accounts on this host. -local function may_invite_new_users(jid, context) - if module.may then - return module:may(":invite-new-users", context); - elseif usermanager.get_roles then -- COMPAT w/0.12 - local user_roles = usermanager.get_roles(jid, module.host); - if not user_roles then - -- User has no roles we can check, just return default - return allow_user_invites; - end - - if user_roles["prosody:admin"] then - return true; - end - if allow_user_invite_roles then - for allowed_role in allow_user_invite_roles do - if user_roles[allowed_role] then - return true; - end - end - end - if deny_user_invite_roles then - for denied_role in deny_user_invite_roles do - if user_roles[denied_role] then - return false; - end - end - end - elseif usermanager.is_admin(jid, module.host) then -- COMPAT w/0.11 - return true; -- Admins may always create invitations - end - -- No role matches, so whatever the default is - return allow_user_invites; -end - -module:depends("adhoc"); - --- This command is available to all local users, even if allow_user_invites = false --- If allow_user_invites is false, creating an invite still works, but the invite will --- not be valid for registration on the current server, only for establishing a roster --- subscription. -module:provides("adhoc", new_adhoc("Create new contact invite", "urn:xmpp:invite#invite", - function (_, data) - local username, host = split_jid(data.from); - if host ~= module.host then - return { - status = "completed"; - error = { - message = "This command is only available to users of "..module.host; - }; - }; - end - local invite = invites.create_contact(username, may_invite_new_users(data.from, data), { - source = data.from - }); - --TODO: check errors - return { - status = "completed"; - form = { - layout = invite_result_form; - values = { - uri = invite.uri; - url = invite.landing_page; - expire = datetime.datetime(invite.expires); - }; - }; - }; - end, allow_contact_invites and "local_user" or "admin")); - --- This is an admin-only command that creates a new invitation suitable for registering --- a new account. It does not add the new user to the admin's roster. -module:provides("adhoc", new_adhoc("Create new account invite", "urn:xmpp:invite#create-account", - function (_, data) - local invite = invites.create_account(nil, { - source = data.from - }); - --TODO: check errors - return { - status = "completed"; - form = { - layout = invite_result_form; - values = { - uri = invite.uri; - url = invite.landing_page; - expire = datetime.datetime(invite.expires); - }; - }; - }; - end, "admin"));
--- a/mod_invites_register/README.md Sat Feb 07 12:25:52 2026 +0100 +++ b/mod_invites_register/README.md Sat Feb 07 19:48:30 2026 +0100 @@ -2,69 +2,12 @@ labels: - 'Stage-Merged' summary: 'Allow account registration using invite tokens' +superseded_by: mod_invites_register ... Introduction ============ -::: {.alert .alert-info} -This module has been merged into Prosody as -[mod_invites_register][doc:modules:mod_invites_register]. Users of -Prosody **0.12** and later should not install this version. +::: {.alert .alert-error} +This module has been merged into Prosody as [mod_invites_register][doc:modules:mod_invites_register]. ::: - -This module is part of the suite of modules that implement invite-based -account registration for Prosody. The other modules are: - -- [mod_invites][doc:modules:mod_invites] -- [mod_invites_adhoc][doc:modules:mod_invites_adhoc] -- [mod_invites_page] -- [mod_invites_register_web] -- [mod_invites_api] -- [mod_register_apps] - -For details and a full overview, start with the [mod_invites] documentation. - -Details -======= - -This module allows clients to register an account using an invite ('preauth') -token generated by mod_invites. It implements the protocol described at -[docs.modernxmpp.org/client/invites](https://docs.modernxmpp.org/client/invites) -and [XEP-0401 version 0.3.0](https://xmpp.org/extensions/attic/xep-0401-0.3.0.html). - -**Note to developers:** the XEP-0401 protocol is expected to change in the future, -though Prosody will attempt to maintain backwards compatibility with the 0.3.0 protocol -for as long as necessary. - -This module is also responsible for implementing the optional server-side part -of [XEP-0379: Pre-Authenticated Roster Subscriptions](https://xmpp.org/extensions/xep-0379.html). - -**Note to admins:** Loading this module will disable registration for users -without an invite token by default. Control this behaviour - -# Configuration - -| Name | Description | Default | -|--------------------------|----------------------------------------------------------|---------| -| registration_invite_only | Require an invitation token for all account registration | `true` | - -## Example: Invite-only registration - -This setup enables registration **only** for users that have a valid -invite token. - -``` {.lua} -allow_registration = true -registration_invite_only = true -``` - -## Example: Open registration - -This setup allows completely **open registration**, even without -an invite token. - -``` {.lua} -allow_registration = true -registration_invite_only = false -```
--- a/mod_invites_register/mod_invites_register.lua Sat Feb 07 12:25:52 2026 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,161 +0,0 @@ -local st = require "util.stanza"; -local jid_split = require "util.jid".split; -local jid_bare = require "util.jid".bare; -local rostermanager = require "core.rostermanager"; - -local require_encryption = module:get_option_boolean("c2s_require_encryption", - module:get_option_boolean("require_encryption", false)); -local invite_only = module:get_option_boolean("registration_invite_only", true); - -local invites; -if prosody.shutdown then -- COMPAT hack to detect prosodyctl - invites = module:depends("invites"); -end - -local legacy_invite_stream_feature = st.stanza("register", { xmlns = "urn:xmpp:invite" }):up(); -local invite_stream_feature = st.stanza("register", { xmlns = "urn:xmpp:ibr-token:0" }):up(); -module:hook("stream-features", function(event) - local session, features = event.origin, event.features; - - -- Advertise to unauthorized clients only. - if session.type ~= "c2s_unauthed" or (require_encryption and not session.secure) then - return - end - - features:add_child(legacy_invite_stream_feature); - features:add_child(invite_stream_feature); -end); - --- XEP-0379: Pre-Authenticated Roster Subscription -module:hook("presence/bare", function (event) - local stanza = event.stanza; - if stanza.attr.type ~= "subscribe" then return end - - local preauth = stanza:get_child("preauth", "urn:xmpp:pars:0"); - if not preauth then return end - local token = preauth.attr.token; - if not token then return end - - local username, host = jid_split(stanza.attr.to); - - local invite, err = invites.get(token, username); - - if not invite then - module:log("debug", "Got invalid token, error: %s", err); - return; - end - - local contact = jid_bare(stanza.attr.from); - - module:log("debug", "Approving inbound subscription to %s from %s", username, contact); - if rostermanager.set_contact_pending_in(username, host, contact, stanza) then - if rostermanager.subscribed(username, host, contact) then - invite:use(); - rostermanager.roster_push(username, host, contact); - - -- Send back a subscription request (goal is mutual subscription) - if not rostermanager.is_user_subscribed(username, host, contact) - and not rostermanager.is_contact_pending_out(username, host, contact) then - module:log("debug", "Sending automatic subscription request to %s from %s", contact, username); - if rostermanager.set_contact_pending_out(username, host, contact) then - rostermanager.roster_push(username, host, contact); - module:send(st.presence({type = "subscribe", from = username.."@"..host, to = contact })); - else - module:log("warn", "Failed to set contact pending out for %s", username); - end - end - end - end -end, 1); - --- Client is submitting a preauth token to allow registration -module:hook("stanza/iq/urn:xmpp:pars:0:preauth", function(event) - local preauth = event.stanza.tags[1]; - local token = preauth.attr.token; - local validated_invite = invites.get(token); - if not validated_invite then - local reply = st.error_reply(event.stanza, "cancel", "forbidden", "The invite token is invalid or expired"); - event.origin.send(reply); - return true; - end - event.origin.validated_invite = validated_invite; - local reply = st.reply(event.stanza); - event.origin.send(reply); - return true; -end); - --- Registration attempt - ensure a valid preauth token has been supplied -module:hook("user-registering", function (event) - local validated_invite = event.validated_invite or (event.session and event.session.validated_invite); - if invite_only and not validated_invite then - event.allowed = false; - event.reason = "Registration on this server is through invitation only"; - return; - elseif not validated_invite then - -- This registration is not using an invite, but - -- the server is not in invite-only mode, so nothing - -- for this module to do... - return; - end - if validated_invite and validated_invite.additional_data and validated_invite.additional_data.allow_reset then - event.allow_reset = validated_invite.additional_data.allow_reset; - end -end); - --- Make a *one-way* subscription. User will see when contact is online, --- contact will not see when user is online. -function subscribe(host, user_username, contact_username) - local user_jid = user_username.."@"..host; - local contact_jid = contact_username.."@"..host; - -- Update user's roster to say subscription request is pending... - rostermanager.set_contact_pending_out(user_username, host, contact_jid); - -- Update contact's roster to say subscription request is pending... - rostermanager.set_contact_pending_in(contact_username, host, user_jid); - -- Update contact's roster to say subscription request approved... - rostermanager.subscribed(contact_username, host, user_jid); - -- Update user's roster to say subscription request approved... - rostermanager.process_inbound_subscription_approval(user_username, host, contact_jid); -end - --- Make a mutual subscription between jid1 and jid2. Each JID will see --- when the other one is online. -function subscribe_both(host, user1, user2) - subscribe(host, user1, user2); - subscribe(host, user2, user1); -end - --- Registration successful, if there was a preauth token, mark it as used -module:hook("user-registered", function (event) - local validated_invite = event.validated_invite or (event.session and event.session.validated_invite); - if not validated_invite then - return; - end - local inviter_username = validated_invite.inviter; - local contact_username = event.username; - validated_invite:use(); - - if inviter_username then - module:log("debug", "Creating mutual subscription between %s and %s", inviter_username, contact_username); - subscribe_both(module.host, inviter_username, contact_username); - rostermanager.roster_push(inviter_username, module.host, contact_username.."@"..module.host); - end - - if validated_invite.additional_data then - module:log("debug", "Importing roles from invite"); - local roles = validated_invite.additional_data.roles; - if roles then - module:open_store("roles"):set(contact_username, roles); - end - end -end); - --- Equivalent of user-registered but for when the account already existed --- (i.e. password reset) -module:hook("user-password-reset", function (event) - local validated_invite = event.validated_invite or (event.session and event.session.validated_invite); - if not validated_invite then - return; - end - validated_invite:use(); -end); -
