Mercurial > prosody-modules
comparison mod_invites/mod_invites.lua @ 3776:80830d97da81
mod_invites: New module providing an API to create/manage invite tokens
| author | Matthew Wild <mwild1@gmail.com> |
|---|---|
| date | Fri, 27 Dec 2019 10:31:33 +0000 |
| parents | |
| children | f85ea76447dd |
comparison
equal
deleted
inserted
replaced
| 3775:d60efdb947fd | 3776:80830d97da81 |
|---|---|
| 1 local id = require "util.id"; | |
| 2 local url = require "socket.url"; | |
| 3 local jid_node = require "util.jid".node; | |
| 4 | |
| 5 local invite_ttl = module:get_option_number("invite_expiry", 86400 * 7); | |
| 6 | |
| 7 local token_storage = module:open_store("invite_token", "map"); | |
| 8 | |
| 9 local function get_uri(action, jid, token, params) --> string | |
| 10 return url.build({ | |
| 11 scheme = "xmpp", | |
| 12 path = jid, | |
| 13 query = action..";preauth="..token..(params and (";"..params) or ""), | |
| 14 }); | |
| 15 end | |
| 16 | |
| 17 local function create_invite(invite_action, invite_jid, allow_registration) | |
| 18 local token = id.medium(); | |
| 19 | |
| 20 local created_at = os.time(); | |
| 21 local expires = created_at + invite_ttl; | |
| 22 | |
| 23 local invite_params = (invite_action == "roster" and allow_registration) and "ibr=y" or nil; | |
| 24 | |
| 25 local invite = { | |
| 26 type = invite_action; | |
| 27 jid = invite_jid; | |
| 28 | |
| 29 token = token; | |
| 30 allow_registration = allow_registration; | |
| 31 | |
| 32 uri = get_uri(invite_action, invite_jid, token, invite_params); | |
| 33 | |
| 34 created_at = created_at; | |
| 35 expires = expires; | |
| 36 }; | |
| 37 | |
| 38 module:fire_event("invite-created", invite); | |
| 39 | |
| 40 if allow_registration then | |
| 41 local ok, err = token_storage:set(nil, token, invite); | |
| 42 if not ok then | |
| 43 module:log("warn", "Failed to store account invite: %s", err); | |
| 44 return nil, "internal-server-error"; | |
| 45 end | |
| 46 end | |
| 47 | |
| 48 if invite_action == "roster" then | |
| 49 local username = jid_node(invite_jid); | |
| 50 local ok, err = token_storage:set(username, token, expires); | |
| 51 if not ok then | |
| 52 module:log("warn", "Failed to store subscription invite: %s", err); | |
| 53 return nil, "internal-server-error"; | |
| 54 end | |
| 55 end | |
| 56 | |
| 57 return invite; | |
| 58 end | |
| 59 | |
| 60 -- Create invitation to register an account (optionally restricted to the specified username) | |
| 61 function create_account(account_username) --luacheck: ignore 131/create_account | |
| 62 local jid = account_username and (account_username.."@"..module.host) or module.host; | |
| 63 return create_invite("register", jid, true); | |
| 64 end | |
| 65 | |
| 66 -- Create invitation to become a contact of a local user | |
| 67 function create_contact(username, allow_registration) --luacheck: ignore 131/create_contact | |
| 68 return create_invite("roster", username.."@"..module.host, allow_registration); | |
| 69 end | |
| 70 | |
| 71 local valid_invite_methods = {}; | |
| 72 local valid_invite_mt = { __index = valid_invite_methods }; | |
| 73 | |
| 74 function valid_invite_methods:use() | |
| 75 if self.username then | |
| 76 -- Also remove the contact invite if present, on the | |
| 77 -- assumption that they now have a mutual subscription | |
| 78 token_storage:set(self.username, self.token, nil); | |
| 79 end | |
| 80 token_storage:set(nil, self.token, nil); | |
| 81 return true; | |
| 82 end | |
| 83 | |
| 84 -- Get a validated invite (or nil, err). Must call :use() on the | |
| 85 -- returned invite after it is actually successfully used | |
| 86 -- For "roster" invites, the username of the local user (who issued | |
| 87 -- the invite) must be passed. | |
| 88 -- If no username is passed, but the registration is a roster invite | |
| 89 -- from a local user, the "inviter" field of the returned invite will | |
| 90 -- be set to their username. | |
| 91 function get(token, username) | |
| 92 if not token then | |
| 93 return nil, "no-token"; | |
| 94 end | |
| 95 | |
| 96 local valid_until, inviter; | |
| 97 | |
| 98 if username then -- token being used for subscription | |
| 99 -- Fetch from user store (subscription invite) | |
| 100 valid_until = token_storage:get(username, token); | |
| 101 else -- token being used for account creation | |
| 102 -- Fetch from host store (account invite) | |
| 103 local token_info = token_storage:get(nil, token); | |
| 104 valid_until = token_info and token_info.expires; | |
| 105 if token_info.type == "roster" then | |
| 106 username = jid_node(token_info.jid); | |
| 107 inviter = username; | |
| 108 end | |
| 109 end | |
| 110 | |
| 111 if not valid_until then | |
| 112 module:log("debug", "Got unknown token: %s", token); | |
| 113 return nil, "token-invalid"; | |
| 114 elseif os.time() > valid_until then | |
| 115 module:log("debug", "Got expired token: %s", token); | |
| 116 return nil, "token-expired"; | |
| 117 end | |
| 118 | |
| 119 return setmetatable({ | |
| 120 token = token; | |
| 121 username = username; | |
| 122 inviter = inviter; | |
| 123 }, valid_invite_mt); | |
| 124 end | |
| 125 | |
| 126 function use(token) --luacheck: ignore 131/use | |
| 127 local invite = get(token); | |
| 128 return invite and invite:use(); | |
| 129 end |
