Software /
code /
prosody-modules
Comparison
mod_invites_api/mod_invites_api.lua @ 5595:f7410850941f
mod_invites_api: Change and add new commands for `module.command` to improve UX.
author | Trần H. Trung <xmpp:trần.h.trung@trung.fun> |
---|---|
date | Wed, 26 Jul 2023 18:43:45 +0700 |
parent | 5143:1cae382e88a1 |
child | 5628:a74b07764d3f |
comparison
equal
deleted
inserted
replaced
5594:14480ca9576e | 5595:f7410850941f |
---|---|
1 local http_formdecode = require "net.http".formdecode; | 1 local http_formdecode = require "net.http".formdecode; |
2 | 2 local jid = require "util.jid"; |
3 local api_key_store; | 3 local usermanager = require "core.usermanager"; |
4 local invites; | 4 local datetime = require "util.datetime"; |
5 | |
6 -- Whether local users can invite other users to create an account on this server | |
7 local allow_user_invites = module:get_option_boolean("allow_user_invites", false); | |
8 -- Who can see and use the contact invite command. It is strongly recommened to | |
9 -- keep this available to all local users. To allow/disallow invite-registration | |
10 -- on the server, use the option above instead. | |
11 local allow_contact_invites = module:get_option_boolean("allow_contact_invites", true); | |
12 | |
13 local api_key_store, api_key_store_kv, invites, date, config, path; | |
5 -- COMPAT: workaround to avoid executing inside prosodyctl | 14 -- COMPAT: workaround to avoid executing inside prosodyctl |
6 if prosody.shutdown then | 15 if prosody.shutdown then |
7 module:depends("http"); | 16 module:depends("http"); |
8 api_key_store = module:open_store("invite_api_keys", "map"); | 17 api_key_store = module:open_store("invite_api_keys", "map"); |
9 invites = module:depends("invites"); | 18 invites = module:depends("invites"); |
19 api_key_store_kv = module:open_store("invite_api_keys"); | |
10 end | 20 end |
11 | 21 |
12 local function get_api_user(request, params) | 22 local function get_api_user(request, params) |
13 local combined_key; | 23 local combined_key; |
14 | 24 |
56 | 66 |
57 if api_user.allowed_methods and not api_user.allowed_methods[event.request.method] then | 67 if api_user.allowed_methods and not api_user.allowed_methods[event.request.method] then |
58 return 405; | 68 return 405; |
59 end | 69 end |
60 | 70 |
61 local invite = invites.create_account(nil, { source = "api/token/"..api_user.id }); | 71 local invite; |
72 local username, domain = jid.prepped_split(api_user.jid); | |
73 if username and usermanager.user_exists(username, domain) then | |
74 local ttl = module:get_option_number("invite_expiry", 86400 * 7); | |
75 invite = invites.create_contact(username, | |
76 allow_user_invites, -- allow_registration | |
77 { source = "api/token/"..api_user.id }, -- additional_data | |
78 ttl | |
79 ); | |
80 else | |
81 invite = invites.create_account(nil, { source = "api/token/"..api_user.id }); | |
82 end | |
62 if not invite then | 83 if not invite then |
63 return 500; | 84 return 500; |
64 end | 85 end |
65 | 86 |
66 event.response.headers.Location = invite.landing_page or invite.uri; | 87 event.response.headers.Location = invite.landing_page or invite.uri; |
77 ["GET"] = handle_request; | 98 ["GET"] = handle_request; |
78 }; | 99 }; |
79 }); | 100 }); |
80 end | 101 end |
81 | 102 |
103 function get_value(callback) | |
104 for key_id, key_info in pairs(api_key_store_kv:get(nil) or {}) do | |
105 date = datetime.datetime(key_info.created_at); | |
106 callback(key_id, key_info); | |
107 end | |
108 end | |
109 | |
110 local function get_url(id, token) | |
111 local config, path = module:get_option("http_paths"); | |
112 if config then | |
113 for k, v in pairs(config) do | |
114 if k == module.name then | |
115 path = v; | |
116 break; | |
117 else | |
118 path = "/"..module.name; | |
119 end | |
120 end | |
121 else path = "/"..module.name ; end | |
122 | |
123 local url_base = module:get_option_string("http_external_link", module.host); | |
124 return url_base..path.."?key="..id.."/"..token.."&redirect=true"; | |
125 end | |
126 | |
82 function module.command(arg) | 127 function module.command(arg) |
83 if #arg < 2 then | 128 if #arg < 2 then |
84 print("Usage:"); | 129 print("========================================================================"); |
85 print(""); | 130 print(""); |
86 print(" prosodyctl mod_"..module.name.." create NAME"); | 131 print("Create:"); |
87 print(" prosodyctl mod_"..module.name.." delete KEY_ID"); | 132 print(""); |
88 print(" prosodyctl mod_"..module.name.." list"); | 133 print("> prosodyctl mod_"..module.name.." create JID NAME"); |
89 print(""); | 134 print(""); |
135 print("Query:"); | |
136 print(""); | |
137 print("> prosodyctl mod_"..module.name.." get JID NAME"); | |
138 print("> prosodyctl mod_"..module.name.." date JID NAME"); | |
139 print("> prosodyctl mod_"..module.name.." id JID NAME"); | |
140 print("> prosodyctl mod_"..module.name.." key JID NAME"); | |
141 print("> prosodyctl mod_"..module.name.." url JID NAME"); | |
142 print(""); | |
143 print("Revoke:"); | |
144 print(""); | |
145 print("> prosodyctl mod_"..module.name.." delete JID NAME"); | |
146 print("> prosodyctl mod_"..module.name.." delete-id HOST ID"); | |
147 print(""); | |
148 print("Renew:"); | |
149 print(""); | |
150 print("> prosodyctl mod_"..module.name.." renew JID NAME"); | |
151 print(""); | |
152 print("Help:"); | |
153 print(""); | |
154 print("> prosodyctl mod_"..module.name.." help COMMAND"); | |
155 print(""); | |
156 print("========================================================================"); | |
157 return; | |
90 end | 158 end |
91 | 159 |
92 local command = table.remove(arg, 1); | 160 local command = table.remove(arg, 1); |
93 | 161 if command == "help" then |
94 local host = table.remove(arg, 1); | 162 help_command = table.remove(arg, 1); |
95 if not prosody.hosts[host] then | 163 if help_command == "create" then |
96 print("Error: please supply a valid host"); | 164 print("========================================================================"); |
165 print(""); | |
166 print("Create a key:"); | |
167 print(""); | |
168 print("> prosodyctl mod_"..module.name.." create JID NAME"); | |
169 print(""); | |
170 print("------------------------------------------------------------------------"); | |
171 print(""); | |
172 print("Usage:"); | |
173 print(""); | |
174 print(" `JID` can either be a user's account or a host."); | |
175 print(" When `JID` is a host, you need to supply a `NAME`."); | |
176 print(""); | |
177 print(" Each user's account can only have 1 API key but hosts are unlimited."); | |
178 print(""); | |
179 print("========================================================================"); | |
180 elseif help_command == "renew" then | |
181 print("========================================================================"); | |
182 print(""); | |
183 print("Re-new a key:"); | |
184 print(""); | |
185 print("> prosodyctl mod_"..module.name.." create JID NAME"); | |
186 print(""); | |
187 print("------------------------------------------------------------------------"); | |
188 print(""); | |
189 print("Usage:"); | |
190 print(""); | |
191 print(" `JID` can either be a user's account or a host."); | |
192 print(" When `JID` is a host, you need to supply a `NAME`."); | |
193 print(""); | |
194 print(" The old `ID` will be kept and a new token will be generated for the API"); | |
195 print(" key you specified."); | |
196 print(""); | |
197 print("========================================================================"); | |
198 elseif help_command == "get" then | |
199 print("========================================================================"); | |
200 print(""); | |
201 print("Get info of a key:"); | |
202 print(""); | |
203 print("> prosodyctl mod_"..module.name.." get JID NAME"); | |
204 print(""); | |
205 print("------------------------------------------------------------------------"); | |
206 print(""); | |
207 print("Usage:"); | |
208 print(""); | |
209 print(" When `JID` is a domain, it will list all the keys under that host."); | |
210 print(" When `JID` is a user's account, it fetches the key for that user."); | |
211 print(" If you supply both a host and a `NAME`, it fetches the key with `NAME`"); | |
212 print(" under that host.") | |
213 print(""); | |
214 print(" Output for a host is: DATE, ID, JID, NAME."); | |
215 print(""); | |
216 print(" Output for a user's account is: DATE, ID, URL."); | |
217 print(""); | |
218 print(" Output for a host with a valid `NAME` is: DATE, ID, URL."); | |
219 print(""); | |
220 print("========================================================================"); | |
221 elseif help_command == "date" then | |
222 print("========================================================================"); | |
223 print(""); | |
224 print("Get the time stamp of a key:"); | |
225 print(""); | |
226 print("> prosodyctl mod_"..module.name.." date JID NAME"); | |
227 print(""); | |
228 print("------------------------------------------------------------------------"); | |
229 print(""); | |
230 print("Usage:"); | |
231 print(""); | |
232 print(" Same as the `get` command but print only the birthday of the key."); | |
233 print(""); | |
234 print("========================================================================"); | |
235 elseif help_command == "id" then | |
236 print("========================================================================"); | |
237 print(""); | |
238 print("Print the ID of a key:"); | |
239 print(""); | |
240 print("> prosodyctl mod_"..module.name.." id JID NAME"); | |
241 print(""); | |
242 print("------------------------------------------------------------------------"); | |
243 print(""); | |
244 print("Usage:"); | |
245 print(""); | |
246 print(" Same as the `get` command but print only the ID of the key."); | |
247 print(""); | |
248 print("========================================================================"); | |
249 elseif help_command == "key" then | |
250 print("========================================================================"); | |
251 print(""); | |
252 print("Print the API key:"); | |
253 print(""); | |
254 print("> prosodyctl mod_"..module.name.." key JID NAME"); | |
255 print(""); | |
256 print("------------------------------------------------------------------------"); | |
257 print(""); | |
258 print("Usage:"); | |
259 print(""); | |
260 print(" Same as the `get` command but print only the key."); | |
261 print(""); | |
262 print(" The key has the format: ID/TOKEN"); | |
263 print(""); | |
264 print(" The `renew` command will generate a new token and revoke the old one."); | |
265 print(""); | |
266 print("========================================================================"); | |
267 elseif help_command == "url" then | |
268 print("========================================================================"); | |
269 print(""); | |
270 print("Print the URL of a key:"); | |
271 print(""); | |
272 print("> prosodyctl mod_"..module.name.." url JID NAME"); | |
273 print(""); | |
274 print("------------------------------------------------------------------------"); | |
275 print(""); | |
276 print("Usage:"); | |
277 print(""); | |
278 print(" Same as the `get` command but print only the URL of the key."); | |
279 print(""); | |
280 print("========================================================================"); | |
281 elseif help_command == "delete" then | |
282 print("========================================================================"); | |
283 print(""); | |
284 print("Delete a key by JID and NAME:"); | |
285 print(""); | |
286 print("> prosodyctl mod_"..module.name.." delete JID NAME"); | |
287 print(""); | |
288 print("------------------------------------------------------------------------"); | |
289 print(""); | |
290 print("Usage:"); | |
291 print(""); | |
292 print(" Same as `create` command but delete the key specified."); | |
293 print(""); | |
294 print("========================================================================"); | |
295 elseif help_command == "delete-id" then | |
296 print("========================================================================"); | |
297 print(""); | |
298 print("Delete API key by ID:"); | |
299 print(""); | |
300 print("> prosodyctl mod_"..module.name.." delete HOST ID"); | |
301 print(""); | |
302 print("------------------------------------------------------------------------"); | |
303 print(""); | |
304 print("Usage:"); | |
305 print(""); | |
306 print(" Input must be a valid host - cannot be a user's account."); | |
307 print(""); | |
308 print(" To get the ID of a key, use the `id` command."); | |
309 print(""); | |
310 print("========================================================================"); | |
311 end | |
312 return; | |
313 end | |
314 | |
315 local username, domain = jid.prepped_split(table.remove(arg, 1)); | |
316 if not prosody.hosts[domain] then | |
317 print("Error: please supply a valid host."); | |
97 return 1; | 318 return 1; |
98 end | 319 end |
99 require "core.storagemanager".initialize_host(host); | 320 require "core.storagemanager".initialize_host(domain); |
100 module.host = host; --luacheck: ignore 122/module | 321 module.host = domain; --luacheck: ignore 122/module |
322 usermanager.initialize_host(module.host); | |
101 api_key_store = module:open_store("invite_api_keys", "map"); | 323 api_key_store = module:open_store("invite_api_keys", "map"); |
324 api_key_store_kv = module:open_store("invite_api_keys"); | |
102 | 325 |
103 if command == "create" then | 326 if command == "create" then |
104 local id = require "util.id".short(); | 327 local util_id = require "util.id".short(); |
105 local token = require "util.id".long(); | 328 local util_token = require "util.id".long(); |
106 api_key_store:set(nil, id, { | 329 local found = false; |
107 id = id; | 330 local os_time = os.time(); |
108 token = token; | 331 if username then |
109 name = arg[1]; | 332 if usermanager.user_exists(username, module.host) then |
110 created_at = os.time(); | 333 get_value(function (id, info) |
111 allowed_methods = { GET = true, POST = true }; | 334 if username.."@"..module.host == info.jid then |
112 }); | 335 date = datetime.datetime(info.created_at); |
113 print(id.."/"..token); | 336 print("Found:"); |
337 print(date, id, info.jid); | |
338 util_id = id; | |
339 util_token = info.token; | |
340 found = true; | |
341 end | |
342 end); | |
343 if found == false then | |
344 api_key_store:set(nil, util_id, { | |
345 id = util_id; | |
346 token = util_token; | |
347 name = nil; | |
348 jid = username.."@"..domain; | |
349 created_at = os_time; | |
350 allowed_methods = { GET = true, POST = true }; | |
351 }); | |
352 date = datetime.datetime(os_time); | |
353 print("Created:"); | |
354 print(date, util_id.."/"..util_token, username.."@"..domain); | |
355 end | |
356 return; | |
357 else | |
358 print("Error: "..username.."@"..domain.." does not exists."); | |
359 return 1; | |
360 end | |
361 elseif domain then | |
362 local arg_name = table.remove(arg, 1); | |
363 if not arg_name or arg_name == "" then | |
364 print("Error: key for host needs a `NAME`."); | |
365 return; | |
366 end | |
367 found = false; | |
368 get_value(function (id, info) | |
369 if domain == info.jid and arg_name == info.name then | |
370 date = datetime.datetime(info.created_at); | |
371 print("Found:"); | |
372 print(date, id, info.jid, info.name); | |
373 util_id = id; | |
374 util_token = info.token; | |
375 found = true; | |
376 end | |
377 end); | |
378 if found == false then | |
379 api_key_store:set(nil, util_id, { | |
380 id = util_id; | |
381 token = util_token; | |
382 name = arg_name; | |
383 jid = domain; | |
384 created_at = os_time; | |
385 allowed_methods = { GET = true, POST = true }; | |
386 }); | |
387 date = datetime.datetime(os_time); | |
388 print("Created:"); | |
389 print(date, util_id.."/"..util_token, domain, arg_name); | |
390 end | |
391 return; | |
392 end | |
393 elseif command == "renew" then | |
394 local util_token = require "util.id".long(); | |
395 local os_time = os.time(); | |
396 if username then | |
397 local found; | |
398 if usermanager.user_exists(username, module.host) then | |
399 get_value(function (id, info) | |
400 if username.."@"..module.host == info.jid then | |
401 api_key_store:set(nil, id, { | |
402 id = id; | |
403 token = util_token; | |
404 name = arg[1]; | |
405 jid = username.."@"..domain; | |
406 created_at = os_time; | |
407 allowed_methods = { GET = true, POST = true }; | |
408 }); | |
409 found = true; | |
410 date = datetime.datetime(os_time); | |
411 print("Re-newed:"); | |
412 print(date, id.."/"..util_token, info.jid); | |
413 end | |
414 end); | |
415 if not found then | |
416 print("Error: Could not find the key for "..username.."@"..domain); | |
417 print(""); | |
418 print("To make this API key, run:"); | |
419 print("prosodyctl "..module.name.." create "..username.."@"..domain); | |
420 return; | |
421 end | |
422 return; | |
423 else | |
424 print("Error: "..username.."@"..domain.." does not exists."); | |
425 return 1; | |
426 end | |
427 elseif domain then | |
428 local arg_name = table.remove(arg, 1); | |
429 if not arg_name or arg_name == "" then | |
430 print("Error: key for host needs a `NAME`."); | |
431 return 1; | |
432 end | |
433 found = false; | |
434 get_value(function (id, info) | |
435 if domain == info.jid and arg_name == info.name then | |
436 api_key_store:set(nil, id, { | |
437 id = id; | |
438 token = util_token; | |
439 name = arg_name; | |
440 jid = domain; | |
441 created_at = os_time; | |
442 allowed_methods = { GET = true, POST = true }; | |
443 }); | |
444 date = datetime.datetime(os_time); | |
445 print("Re-newed:"); | |
446 print(date, id.."/"..util_token, info.jid, info.name); | |
447 found = true; | |
448 end | |
449 end); | |
450 if not found then | |
451 date = datetime.datetime(os_time); | |
452 print("Error: Could not find "..arg_name.." in "..domain); | |
453 print(""); | |
454 print("To make this API key, run:"); | |
455 print("prosodyctl "..module.name.." create "..domain.." "..arg_name); | |
456 return; | |
457 end | |
458 return; | |
459 end | |
460 elseif command == "get" then | |
461 local name = table.remove(arg, 1); | |
462 local found = false; | |
463 if name and not username then | |
464 get_value(function (id, info) | |
465 if info.name == name then | |
466 print(date, id, get_url(id, info.token)); | |
467 found = true; | |
468 end | |
469 end); | |
470 if found == false then | |
471 print("Error: could not find "..name.." in "..domain); | |
472 print(""); | |
473 print("You can create it with:"); | |
474 print(""); | |
475 print("> prosodyctl "..module.name.." create "..domain.." "..name); | |
476 end | |
477 return; | |
478 elseif username then | |
479 get_value(function (id, info) | |
480 local j = jid.prepped_split(info.jid); | |
481 if j == username then | |
482 print(date, id, get_url(id, info.token)); | |
483 found = true; | |
484 end | |
485 end); | |
486 if found == false then | |
487 print("Error: could not find the key for "..username.."@"..domain); | |
488 print(""); | |
489 print("You can create it with:"); | |
490 print(""); | |
491 print("> prosodyctl "..module.name.." create "..username.."@"..domain); | |
492 end | |
493 return; | |
494 else | |
495 get_value(function (id, info) | |
496 if info.jid == module.host then | |
497 print(date, id, info.jid, info.name or "<unknown>"); | |
498 else | |
499 print(date, id, info.jid, info.name or ""); | |
500 end | |
501 end); | |
502 return; | |
503 end | |
504 elseif command == "date" then | |
505 local name = table.remove(arg, 1); | |
506 if name and not username then | |
507 get_value(function (id, info) | |
508 if info.name == name then | |
509 print(date); | |
510 end | |
511 end); | |
512 return; | |
513 elseif username then | |
514 get_value(function (id, info) | |
515 local j = jid.prepped_split(info.jid); | |
516 if j == username then | |
517 print(date); | |
518 end | |
519 end); | |
520 return; | |
521 else | |
522 get_value(function (id, info) | |
523 if info.jid == module.host then | |
524 print(date, info.jid, info.name or "<unknown>"); | |
525 else | |
526 print(date, info.jid); | |
527 end | |
528 end); | |
529 return; | |
530 end | |
531 elseif command == "id" then | |
532 local name = table.remove(arg, 1); | |
533 if name and not username then | |
534 get_value(function (id, info) | |
535 if info.name == name then | |
536 print(id); | |
537 end | |
538 end); | |
539 return; | |
540 elseif username then | |
541 get_value(function (id, info) | |
542 local j = jid.prepped_split(info.jid); | |
543 if j == username then | |
544 print(id); | |
545 end | |
546 end); | |
547 return; | |
548 else | |
549 get_value(function (id, info) | |
550 if info.jid == module.host then | |
551 print(id, info.jid, info.name or "<unknown>"); | |
552 else | |
553 print(id, info.jid); | |
554 end | |
555 end); | |
556 return; | |
557 end | |
558 elseif command == "key" then | |
559 local name = table.remove(arg, 1); | |
560 if name and not username then | |
561 get_value(function (id, info) | |
562 if info.name == name then | |
563 print(id.."/"..info.token); | |
564 end | |
565 end); | |
566 return; | |
567 elseif username then | |
568 get_value(function (id, info) | |
569 local j = jid.prepped_split(info.jid); | |
570 if j == username then | |
571 print(id.."/"..info.token); | |
572 end | |
573 end); | |
574 return; | |
575 else | |
576 get_value(function (id, info) | |
577 if info.jid == module.host then | |
578 print(id.."/"..info.token, info.jid, info.name or "<unknown>"); | |
579 else | |
580 print(id.."/"..info.token, info.jid); | |
581 end | |
582 end); | |
583 return; | |
584 end | |
585 elseif command == "url" then | |
586 local name = table.remove(arg, 1); | |
587 if name and not username then | |
588 get_value(function (id, info) | |
589 if info.name == name then | |
590 print(get_url(id, info.token)); | |
591 end | |
592 end); | |
593 return; | |
594 elseif username then | |
595 get_value(function (id, info) | |
596 local j = jid.prepped_split(info.jid); | |
597 if j == username then | |
598 print(get_url(id, info.token)); | |
599 end | |
600 end); | |
601 return; | |
602 else | |
603 get_value(function (id, info) | |
604 if info.jid == module.host then | |
605 print(get_url(id, info.token), info.jid, info.name or "<unknown>"); | |
606 else | |
607 print(get_url(id, info.token), info.jid); | |
608 end | |
609 end); | |
610 return; | |
611 end | |
114 elseif command == "delete" then | 612 elseif command == "delete" then |
115 local id = table.remove(arg, 1); | 613 local name = table.remove(arg, 1); |
116 if not api_key_store:get(nil, id) then | 614 if name and not username then |
615 get_value(function (id, info) | |
616 if info.name == name then | |
617 api_key_store:set(nil, id, nil); | |
618 print("Deleted:"); | |
619 print(date, id.."/"..info.token, info.jid, info.name or ""); | |
620 end | |
621 end); | |
622 return; | |
623 elseif username then | |
624 get_value(function (id, info) | |
625 local j = jid.prepped_split(info.jid); | |
626 if j == username then | |
627 api_key_store:set(nil, id, nil); | |
628 print("Deleted:"); | |
629 print(date, id.."/"..info.token, info.jid, info.name or ""); | |
630 end | |
631 end); | |
632 return; | |
633 else | |
634 print("Error: Needs a valid `JID`. Or a host and a `NAME`."); | |
635 end | |
636 elseif command == "delete-id" then | |
637 if username then | |
638 print("Error: Input must be a valid host - cannot be a user's account."); | |
639 return 1; | |
640 end | |
641 local arg_id = table.remove(arg, 1); | |
642 if not api_key_store:get(nil, arg_id) then | |
117 print("Error: key not found"); | 643 print("Error: key not found"); |
118 return 1; | 644 return 1; |
119 end | 645 else |
120 api_key_store:set(nil, id, nil); | 646 get_value(function (id, info) |
121 elseif command == "list" then | 647 if arg_id == id then |
122 local api_key_store_kv = module:open_store("invite_api_keys"); | 648 api_key_store:set(nil, id, nil); |
123 for key_id, key_info in pairs(api_key_store_kv:get(nil) or {}) do | 649 print("Deleted:"); |
124 print(key_id, key_info.name or "<unknown>"); | 650 print(date, id, info.jid, info.name or ""); |
651 end | |
652 end); | |
653 return; | |
125 end | 654 end |
126 else | 655 else |
127 print("Unknown command - "..command); | 656 print("Unknown command - "..command); |
128 end | 657 end |
129 end | 658 end |