Comparison

plugins/mod_invites.lua @ 13738:26a0f653793e 13.0

mod_invites: Deprecate 'mod_invites generate' in favour of new shell commands
author Matthew Wild <mwild1@gmail.com>
date Mon, 17 Feb 2025 19:12:40 +0000
parent 13725:c51140dfbc4e
child 13740:4cf2caa63277
comparison
equal deleted inserted replaced
13737:46e7cc4de5e6 13738:26a0f653793e
242 module:add_item("shell-command", { 242 module:add_item("shell-command", {
243 section = "invite"; 243 section = "invite";
244 section_desc = "Create and manage invitations"; 244 section_desc = "Create and manage invitations";
245 name = "create_account"; 245 name = "create_account";
246 desc = "Create an invitation to make an account on this server with the specified JID (supply only a hostname to allow any username)"; 246 desc = "Create an invitation to make an account on this server with the specified JID (supply only a hostname to allow any username)";
247 args = {
248 { name = "user_jid", type = "string" };
249 };
250 host_selector = "user_jid";
251 flags = {
252 array_params = { role = true, group = true };
253 value_params = { expires_after = true };
254 };
255
256 handler = function (self, user_jid, opts) --luacheck: ignore 212/self
257 local username = jid_split(user_jid);
258 local roles = opts.role or {};
259 local groups = opts.group or {};
260
261 if opts.admin then
262 -- Insert it first since we don't get order out of argparse
263 table.insert(roles, 1, "prosody:admin");
264 end
265
266 local ttl;
267 if opts.expires_after then
268 ttl = human_io.parse_duration(opts.expires_after);
269 if not ttl then
270 return false, "Unable to parse duration: "..opts.expires_after;
271 end
272 end
273
274 local invite = assert(create_account(username, {
275 roles = roles;
276 groups = groups;
277 }, ttl));
278
279 return true, invite.landing_page or invite.uri;
280 end;
281 });
282
283 module:add_item("shell-command", {
284 section = "invite";
285 section_desc = "Create and manage invitations";
286 name = "create_reset";
287 desc = "Create a password reset link for the specified user";
247 args = { { name = "user_jid", type = "string" } }; 288 args = { { name = "user_jid", type = "string" } };
248 host_selector = "user_jid"; 289 host_selector = "user_jid";
249 290 flags = {
250 handler = function (self, user_jid) --luacheck: ignore 212/self 291 value_params = { expires_after = true };
292 };
293
294 handler = function (self, user_jid, opts) --luacheck: ignore 212/self
251 local username = jid_split(user_jid); 295 local username = jid_split(user_jid);
252 local invite, err = create_account(username); 296 if not username then
253 if not invite then return nil, err; end 297 return nil, "Supply the JID of the account you want to generate a password reset for";
254 return true, invite.landing_page or invite.uri; 298 end
255 end; 299 local duration_sec = require "prosody.util.human.io".parse_duration(opts and opts.expires_after or "1d");
256 }); 300 if not duration_sec then
257 301 return nil, "Unable to parse duration: "..opts.expires_after;
258 module:add_item("shell-command", { 302 end
259 section = "invite";
260 section_desc = "Create and manage invitations";
261 name = "create_reset";
262 desc = "Create a password reset link for the specified user";
263 args = { { name = "user_jid", type = "string" }, { name = "duration", type = "string" } };
264 host_selector = "user_jid";
265
266 handler = function (self, user_jid, duration) --luacheck: ignore 212/self
267 local username = jid_split(user_jid);
268 local duration_sec = require "prosody.util.human.io".parse_duration(duration or "1d");
269 local invite, err = create_account_reset(username, duration_sec); 303 local invite, err = create_account_reset(username, duration_sec);
270 if not invite then return nil, err; end 304 if not invite then return nil, err; end
271 self.session.print(invite.landing_page or invite.uri); 305 self.session.print(invite.landing_page or invite.uri);
272 return true, ("Password reset link for %s valid until %s"):format(user_jid, os.date("%Y-%m-%d %T", invite.expires)); 306 return true, ("Password reset link for %s valid until %s"):format(user_jid, os.date("%Y-%m-%d %T", invite.expires));
273 end; 307 end;
276 module:add_item("shell-command", { 310 module:add_item("shell-command", {
277 section = "invite"; 311 section = "invite";
278 section_desc = "Create and manage invitations"; 312 section_desc = "Create and manage invitations";
279 name = "create_contact"; 313 name = "create_contact";
280 desc = "Create an invitation to become contacts with the specified user"; 314 desc = "Create an invitation to become contacts with the specified user";
281 args = { { name = "user_jid", type = "string" }, { name = "allow_registration" } }; 315 args = { { name = "user_jid", type = "string" } };
282 host_selector = "user_jid"; 316 host_selector = "user_jid";
283 317 flags = {
284 handler = function (self, user_jid, allow_registration) --luacheck: ignore 212/self 318 value_params = { expires_after = true };
319 kv_params = { allow_registration = true };
320 };
321
322 handler = function (self, user_jid, opts) --luacheck: ignore 212/self
285 local username = jid_split(user_jid); 323 local username = jid_split(user_jid);
286 local invite, err = create_contact(username, allow_registration); 324 if not username then
325 return nil, "Supply the JID of the account you want the recipient to become a contact of";
326 end
327 local ttl;
328 if opts.expires_after then
329 ttl = require "prosody.util.human.io".parse_duration(opts.expires_after);
330 if not ttl then
331 return nil, "Unable to parse duration: "..opts.expires_after;
332 end
333 end
334 local invite, err = create_contact(username, opts.allow_registration, nil, ttl);
287 if not invite then return nil, err; end 335 if not invite then return nil, err; end
288 return true, invite.landing_page or invite.uri; 336 return true, invite.landing_page or invite.uri;
289 end; 337 end;
290 }); 338 });
291 339
440 return 2; 488 return 2;
441 end 489 end
442 return subcommands[cmd](arg); 490 return subcommands[cmd](arg);
443 end 491 end
444 492
445 function subcommands.generate(arg) 493 function subcommands.generate()
446 local function help(short) 494 print("This command is deprecated. Please see 'prosodyctl shell help invite' for available commands.");
447 print("usage: prosodyctl mod_" .. module.name .. " generate DOMAIN --reset USERNAME") 495 return 1;
448 print("usage: prosodyctl mod_" .. module.name .. " generate DOMAIN [--admin] [--role ROLE] [--group GROUPID]...") 496 end
449 if short then return 2 end
450 print()
451 print("This command has two modes: password reset and new account.")
452 print("If --reset is given, the command operates in password reset mode and in new account mode otherwise.")
453 print()
454 print("required arguments in password reset mode:")
455 print()
456 print(" --reset USERNAME Generate a password reset link for the given USERNAME.")
457 print()
458 print("optional arguments in new account mode:")
459 print()
460 print(" --admin Make the new user privileged")
461 print(" Equivalent to --role prosody:admin")
462 print(" --role ROLE Grant the given ROLE to the new user")
463 print(" --group GROUPID Add the user to the group with the given ID")
464 print(" Can be specified multiple times")
465 print(" --expires-after T Time until the invite expires (e.g. '1 week')")
466 print()
467 print("--group can be specified multiple times; the user will be added to all groups.")
468 print()
469 print("--reset and the other options cannot be mixed.")
470 return 2
471 end
472
473 local earlyopts = argparse.parse(arg, { short_params = { h = "help"; ["?"] = "help" } });
474 if earlyopts.help or not earlyopts[1] then
475 return help();
476 end
477
478 local sm = require "prosody.core.storagemanager";
479 local mm = require "prosody.core.modulemanager";
480
481 local host = table.remove(arg, 1); -- pop host
482 if not host then return help(true) end
483 sm.initialize_host(host);
484 module.host = host; --luacheck: ignore 122/module
485 token_storage = module:open_store("invite_token", "map");
486
487 local opts = argparse.parse(arg, {
488 short_params = { h = "help"; ["?"] = "help"; g = "group" };
489 value_params = { group = true; reset = true; role = true };
490 array_params = { group = true; role = true };
491 });
492
493 if opts.help then
494 return help();
495 end
496
497 -- Load mod_invites
498 local invites = module:depends("invites");
499 -- Optional community module that if used, needs to be loaded here
500 local invites_page_module = module:get_option_string("invites_page_module", "invites_page");
501 if mm.get_modules_for_host(host):contains(invites_page_module) then
502 module:depends(invites_page_module);
503 end
504
505 local allow_reset;
506
507 if opts.reset then
508 local nodeprep = require "prosody.util.encodings".stringprep.nodeprep;
509 local username = nodeprep(opts.reset)
510 if not username then
511 print("Please supply a valid username to generate a reset link for");
512 return 2;
513 end
514 allow_reset = username;
515 end
516
517 local roles = opts.role or {};
518 local groups = opts.group or {};
519
520 if opts.admin then
521 -- Insert it first since we don't get order out of argparse
522 table.insert(roles, 1, "prosody:admin");
523 end
524
525 local invite;
526 if allow_reset then
527 if roles[1] then
528 print("--role/--admin and --reset are mutually exclusive")
529 return 2;
530 end
531 if #groups > 0 then
532 print("--group and --reset are mutually exclusive")
533 end
534 invite = assert(invites.create_account_reset(allow_reset));
535 else
536 invite = assert(invites.create_account(nil, {
537 roles = roles,
538 groups = groups
539 }, opts.expires_after and human_io.parse_duration(opts.expires_after)));
540 end
541
542 print(invite.landing_page or invite.uri);
543 end