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
child 4077: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