Comparison

plugins/mod_admin_adhoc.lua @ 11633:77e38ea34d82

mod_admin_adhoc: Log who performs administrative actions Goal is to have some accountability for these privileged actions.
author Kim Alvefur <zash@zash.se>
date Sun, 27 Jun 2021 21:56:45 +0200
parent 11632:21a1b9fb08a1
child 11727:f3aee8a825cc
comparison
equal deleted inserted replaced
11632:21a1b9fb08a1 11633:77e38ea34d82
52 { name = "accountjid", type = "jid-single", required = true, label = "The Jabber ID for the account to be added" }; 52 { name = "accountjid", type = "jid-single", required = true, label = "The Jabber ID for the account to be added" };
53 { name = "password", type = "text-private", label = "The password for this account" }; 53 { name = "password", type = "text-private", label = "The password for this account" };
54 { name = "password-verify", type = "text-private", label = "Retype password" }; 54 { name = "password-verify", type = "text-private", label = "Retype password" };
55 }; 55 };
56 56
57 local add_user_command_handler = adhoc_simple(add_user_layout, function(fields, err) 57 local add_user_command_handler = adhoc_simple(add_user_layout, function(fields, err, data)
58 if err then 58 if err then
59 return generate_error_message(err); 59 return generate_error_message(err);
60 end 60 end
61 local username, host = jid.split(fields.accountjid); 61 local username, host = jid.split(fields.accountjid);
62 if module_host ~= host then 62 if module_host ~= host then
65 if (fields["password"] == fields["password-verify"]) and username and host then 65 if (fields["password"] == fields["password-verify"]) and username and host then
66 if usermanager_user_exists(username, host) then 66 if usermanager_user_exists(username, host) then
67 return { status = "completed", error = { message = "Account already exists" } }; 67 return { status = "completed", error = { message = "Account already exists" } };
68 else 68 else
69 if usermanager_create_user(username, fields.password, host) then 69 if usermanager_create_user(username, fields.password, host) then
70 module:log("info", "Created new account %s@%s", username, host); 70 module:log("info", "Created new account %s@%s by %s", username, host, jid.bare(data.from));
71 return { status = "completed", info = "Account successfully created" }; 71 return { status = "completed", info = "Account successfully created" };
72 else 72 else
73 return { status = "completed", error = { message = "Failed to write data to disk" } }; 73 return { status = "completed", error = { message = "Failed to write data to disk" } };
74 end 74 end
75 end 75 end
87 { name = "FORM_TYPE", type = "hidden", value = "http://jabber.org/protocol/admin" }; 87 { name = "FORM_TYPE", type = "hidden", value = "http://jabber.org/protocol/admin" };
88 { name = "accountjid", type = "jid-single", required = true, label = "The Jabber ID for this account" }; 88 { name = "accountjid", type = "jid-single", required = true, label = "The Jabber ID for this account" };
89 { name = "password", type = "text-private", required = true, label = "The password for this account" }; 89 { name = "password", type = "text-private", required = true, label = "The password for this account" };
90 }; 90 };
91 91
92 local change_user_password_command_handler = adhoc_simple(change_user_password_layout, function(fields, err) 92 local change_user_password_command_handler = adhoc_simple(change_user_password_layout, function(fields, err, data)
93 if err then 93 if err then
94 return generate_error_message(err); 94 return generate_error_message(err);
95 end 95 end
96 local username, host = jid.split(fields.accountjid); 96 local username, host = jid.split(fields.accountjid);
97 if module_host ~= host then 97 if module_host ~= host then
101 message = "Trying to change the password of a user on " .. host .. " but command was sent to " .. module_host 101 message = "Trying to change the password of a user on " .. host .. " but command was sent to " .. module_host
102 } 102 }
103 }; 103 };
104 end 104 end
105 if usermanager_user_exists(username, host) and usermanager_set_password(username, fields.password, host, nil) then 105 if usermanager_user_exists(username, host) and usermanager_set_password(username, fields.password, host, nil) then
106 module:log("info", "Password of account %s@%s changed by %s", username, host, jid.bare(data.from));
106 return { status = "completed", info = "Password successfully changed" }; 107 return { status = "completed", info = "Password successfully changed" };
107 else 108 else
108 return { status = "completed", error = { message = "User does not exist" } }; 109 return { status = "completed", error = { message = "User does not exist" } };
109 end 110 end
110 end); 111 end);
111 112
112 -- Reloading the config 113 -- Reloading the config
113 local function config_reload_handler(self, data, state) 114 local function config_reload_handler(self, data, state)
115 module:log("info", "%s reloads the config", jid.bare(data.from));
114 local ok, err = prosody.reload_config(); 116 local ok, err = prosody.reload_config();
115 if ok then 117 if ok then
116 return { status = "completed", info = "Configuration reloaded (modules may need to be reloaded for this to have an effect)" }; 118 return { status = "completed", info = "Configuration reloaded (modules may need to be reloaded for this to have an effect)" };
117 else 119 else
118 return { status = "completed", error = { message = "Failed to reload config: " .. tostring(err) } }; 120 return { status = "completed", error = { message = "Failed to reload config: " .. tostring(err) } };
126 128
127 { name = "FORM_TYPE", type = "hidden", value = "http://jabber.org/protocol/admin" }; 129 { name = "FORM_TYPE", type = "hidden", value = "http://jabber.org/protocol/admin" };
128 { name = "accountjids", type = "jid-multi", required = true, label = "The Jabber ID(s) to delete" }; 130 { name = "accountjids", type = "jid-multi", required = true, label = "The Jabber ID(s) to delete" };
129 }; 131 };
130 132
131 local delete_user_command_handler = adhoc_simple(delete_user_layout, function(fields, err) 133 local delete_user_command_handler = adhoc_simple(delete_user_layout, function(fields, err, data)
132 if err then 134 if err then
133 return generate_error_message(err); 135 return generate_error_message(err);
134 end 136 end
135 local failed = {}; 137 local failed = {};
136 local succeeded = {}; 138 local succeeded = {};
137 for _, aJID in ipairs(fields.accountjids) do 139 for _, aJID in ipairs(fields.accountjids) do
138 local username, host = jid.split(aJID); 140 local username, host = jid.split(aJID);
139 if (host == module_host) and usermanager_user_exists(username, host) and usermanager_delete_user(username, host) then 141 if (host == module_host) and usermanager_user_exists(username, host) and usermanager_delete_user(username, host) then
140 module:log("debug", "User %s has been deleted", aJID); 142 module:log("info", "User %s has been deleted by %s", aJID, jid.bare(data.from));
141 succeeded[#succeeded+1] = aJID; 143 succeeded[#succeeded+1] = aJID;
142 else 144 else
143 module:log("debug", "Tried to delete non-existant user %s", aJID); 145 module:log("debug", "Tried to delete non-existant user %s", aJID);
144 failed[#failed+1] = aJID; 146 failed[#failed+1] = aJID;
145 end 147 end
471 473
472 { name = "FORM_TYPE", type = "hidden", value = "http://prosody.im/protocol/modules#global-load" }; 474 { name = "FORM_TYPE", type = "hidden", value = "http://prosody.im/protocol/modules#global-load" };
473 { name = "module", type = "text-single", required = true, label = "Module to globally load:"}; 475 { name = "module", type = "text-single", required = true, label = "Module to globally load:"};
474 }; 476 };
475 477
476 local globally_load_module_handler = adhoc_simple(globally_load_module_layout, function(fields, err) 478 local globally_load_module_handler = adhoc_simple(globally_load_module_layout, function(fields, err, data)
477 local ok_list, err_list = {}, {}; 479 local ok_list, err_list = {}, {};
478 480
479 if err then 481 if err then
480 return generate_error_message(err); 482 return generate_error_message(err);
481 end 483 end
487 err_list[#err_list + 1] = module_host .. " (Error: " .. tostring(err) .. ")"; 489 err_list[#err_list + 1] = module_host .. " (Error: " .. tostring(err) .. ")";
488 end 490 end
489 491
490 -- Is this a global module? 492 -- Is this a global module?
491 if modulemanager.is_loaded("*", fields.module) and not modulemanager.is_loaded(module_host, fields.module) then 493 if modulemanager.is_loaded("*", fields.module) and not modulemanager.is_loaded(module_host, fields.module) then
494 module:log("info", "mod_%s loaded by %s", fields.module, jid.bare(data.from));
492 return { status = "completed", info = 'Global module '..fields.module..' loaded.' }; 495 return { status = "completed", info = 'Global module '..fields.module..' loaded.' };
493 end 496 end
494 497
495 -- This is either a shared or "normal" module, load it on all other hosts 498 -- This is either a shared or "normal" module, load it on all other hosts
496 for host_name, host in pairs(hosts) do 499 for host_name, host in pairs(hosts) do
502 err_list[#err_list + 1] = host_name .. " (Error: " .. tostring(err) .. ")"; 505 err_list[#err_list + 1] = host_name .. " (Error: " .. tostring(err) .. ")";
503 end 506 end
504 end 507 end
505 end 508 end
506 509
510 module:log("info", "mod_%s loaded by %s", fields.module, jid.bare(data.from));
507 local info = (#ok_list > 0 and ("The module "..fields.module.." was successfully loaded onto the hosts:\n"..t_concat(ok_list, "\n")) or "") 511 local info = (#ok_list > 0 and ("The module "..fields.module.." was successfully loaded onto the hosts:\n"..t_concat(ok_list, "\n")) or "")
508 .. ((#ok_list > 0 and #err_list > 0) and "\n" or "") .. 512 .. ((#ok_list > 0 and #err_list > 0) and "\n" or "") ..
509 (#err_list > 0 and ("Failed to load the module "..fields.module.." onto the hosts:\n"..t_concat(err_list, "\n")) or ""); 513 (#err_list > 0 and ("Failed to load the module "..fields.module.." onto the hosts:\n"..t_concat(err_list, "\n")) or "");
510 return { status = "completed", info = info }; 514 return { status = "completed", info = info };
511 end); 515 end);
519 { name = "modules", type = "list-multi", required = true, label = "Modules to be reloaded:"}; 523 { name = "modules", type = "list-multi", required = true, label = "Modules to be reloaded:"};
520 }; 524 };
521 525
522 local reload_modules_handler = adhoc_initial(reload_modules_layout, function() 526 local reload_modules_handler = adhoc_initial(reload_modules_layout, function()
523 return { modules = array.collect(keys(hosts[module_host].modules)):sort() }; 527 return { modules = array.collect(keys(hosts[module_host].modules)):sort() };
524 end, function(fields, err) 528 end, function(fields, err, data)
525 if err then 529 if err then
526 return generate_error_message(err); 530 return generate_error_message(err);
527 end 531 end
528 local ok_list, err_list = {}, {}; 532 local ok_list, err_list = {}, {};
529 for _, module in ipairs(fields.modules) do 533 for _, module in ipairs(fields.modules) do
532 ok_list[#ok_list + 1] = module; 536 ok_list[#ok_list + 1] = module;
533 else 537 else
534 err_list[#err_list + 1] = module .. "(Error: " .. tostring(err) .. ")"; 538 err_list[#err_list + 1] = module .. "(Error: " .. tostring(err) .. ")";
535 end 539 end
536 end 540 end
541 module:log("info", "mod_%s reloaded by %s", fields.module, jid.bare(data.from));
537 local info = (#ok_list > 0 and ("The following modules were successfully reloaded on host "..module_host..":\n"..t_concat(ok_list, "\n")) or "") 542 local info = (#ok_list > 0 and ("The following modules were successfully reloaded on host "..module_host..":\n"..t_concat(ok_list, "\n")) or "")
538 .. ((#ok_list > 0 and #err_list > 0) and "\n" or "") .. 543 .. ((#ok_list > 0 and #err_list > 0) and "\n" or "") ..
539 (#err_list > 0 and ("Failed to reload the following modules on host "..module_host..":\n"..t_concat(err_list, "\n")) or ""); 544 (#err_list > 0 and ("Failed to reload the following modules on host "..module_host..":\n"..t_concat(err_list, "\n")) or "");
540 return { status = "completed", info = info }; 545 return { status = "completed", info = info };
541 end); 546 end);
554 for _, host in pairs(hosts) do 559 for _, host in pairs(hosts) do
555 loaded_modules:append(array(keys(host.modules))); 560 loaded_modules:append(array(keys(host.modules)));
556 end 561 end
557 loaded_modules = array(set.new(loaded_modules):items()):sort(); 562 loaded_modules = array(set.new(loaded_modules):items()):sort();
558 return { module = loaded_modules }; 563 return { module = loaded_modules };
559 end, function(fields, err) 564 end, function(fields, err, data)
560 local is_global = false; 565 local is_global = false;
561 566
562 if err then 567 if err then
563 return generate_error_message(err); 568 return generate_error_message(err);
564 end 569 end
589 else 594 else
590 return { status = "completed", info = 'Module '..fields.module..' not loaded on any host.' }; 595 return { status = "completed", info = 'Module '..fields.module..' not loaded on any host.' };
591 end 596 end
592 end 597 end
593 598
599 module:log("info", "mod_%s reloaded by %s", fields.module, jid.bare(data.from));
594 local info = (#ok_list > 0 and ("The module "..fields.module.." was successfully reloaded on the hosts:\n"..t_concat(ok_list, "\n")) or "") 600 local info = (#ok_list > 0 and ("The module "..fields.module.." was successfully reloaded on the hosts:\n"..t_concat(ok_list, "\n")) or "")
595 .. ((#ok_list > 0 and #err_list > 0) and "\n" or "") .. 601 .. ((#ok_list > 0 and #err_list > 0) and "\n" or "") ..
596 (#err_list > 0 and ("Failed to reload the module "..fields.module.." on the hosts:\n"..t_concat(err_list, "\n")) or ""); 602 (#err_list > 0 and ("Failed to reload the module "..fields.module.." on the hosts:\n"..t_concat(err_list, "\n")) or "");
597 return { status = "completed", info = info }; 603 return { status = "completed", info = info };
598 end); 604 end);
638 }; 644 };
639 }; 645 };
640 { name = "announcement", type = "text-multi", label = "Announcement" }; 646 { name = "announcement", type = "text-multi", label = "Announcement" };
641 }; 647 };
642 648
643 local shut_down_service_handler = adhoc_simple(shut_down_service_layout, function(fields, err) 649 local shut_down_service_handler = adhoc_simple(shut_down_service_layout, function(fields, err, data)
644 if err then 650 if err then
645 return generate_error_message(err); 651 return generate_error_message(err);
646 end 652 end
653
654 module:log("info", "Server being shut down by %s", jid.bare(data.from));
647 655
648 if fields.announcement and #fields.announcement > 0 then 656 if fields.announcement and #fields.announcement > 0 then
649 local message = st.message({type = "headline"}, fields.announcement):up() 657 local message = st.message({type = "headline"}, fields.announcement):up()
650 :tag("subject"):text("Server is shutting down"); 658 :tag("subject"):text("Server is shutting down");
651 send_to_online(message); 659 send_to_online(message);
665 { name = "modules", type = "list-multi", required = true, label = "Modules to be unloaded:"}; 673 { name = "modules", type = "list-multi", required = true, label = "Modules to be unloaded:"};
666 }; 674 };
667 675
668 local unload_modules_handler = adhoc_initial(unload_modules_layout, function() 676 local unload_modules_handler = adhoc_initial(unload_modules_layout, function()
669 return { modules = array.collect(keys(hosts[module_host].modules)):sort() }; 677 return { modules = array.collect(keys(hosts[module_host].modules)):sort() };
670 end, function(fields, err) 678 end, function(fields, err, data)
671 if err then 679 if err then
672 return generate_error_message(err); 680 return generate_error_message(err);
673 end 681 end
674 local ok_list, err_list = {}, {}; 682 local ok_list, err_list = {}, {};
675 for _, module in ipairs(fields.modules) do 683 for _, module in ipairs(fields.modules) do
678 ok_list[#ok_list + 1] = module; 686 ok_list[#ok_list + 1] = module;
679 else 687 else
680 err_list[#err_list + 1] = module .. "(Error: " .. tostring(err) .. ")"; 688 err_list[#err_list + 1] = module .. "(Error: " .. tostring(err) .. ")";
681 end 689 end
682 end 690 end
691 module:log("info", "mod_%s unloaded by %s", fields.module, jid.bare(data.from));
683 local info = (#ok_list > 0 and ("The following modules were successfully unloaded on host "..module_host..":\n"..t_concat(ok_list, "\n")) or "") 692 local info = (#ok_list > 0 and ("The following modules were successfully unloaded on host "..module_host..":\n"..t_concat(ok_list, "\n")) or "")
684 .. ((#ok_list > 0 and #err_list > 0) and "\n" or "") .. 693 .. ((#ok_list > 0 and #err_list > 0) and "\n" or "") ..
685 (#err_list > 0 and ("Failed to unload the following modules on host "..module_host..":\n"..t_concat(err_list, "\n")) or ""); 694 (#err_list > 0 and ("Failed to unload the following modules on host "..module_host..":\n"..t_concat(err_list, "\n")) or "");
686 return { status = "completed", info = info }; 695 return { status = "completed", info = info };
687 end); 696 end);
700 for _, host in pairs(hosts) do 709 for _, host in pairs(hosts) do
701 loaded_modules:append(array(keys(host.modules))); 710 loaded_modules:append(array(keys(host.modules)));
702 end 711 end
703 loaded_modules = array(set.new(loaded_modules):items()):sort(); 712 loaded_modules = array(set.new(loaded_modules):items()):sort();
704 return { module = loaded_modules }; 713 return { module = loaded_modules };
705 end, function(fields, err) 714 end, function(fields, err, data)
706 local is_global = false; 715 local is_global = false;
707 if err then 716 if err then
708 return generate_error_message(err); 717 return generate_error_message(err);
709 end 718 end
710 719
734 else 743 else
735 return { status = "completed", info = 'Module '..fields.module..' not loaded on any host.' }; 744 return { status = "completed", info = 'Module '..fields.module..' not loaded on any host.' };
736 end 745 end
737 end 746 end
738 747
748 module:log("info", "mod_%s globally unloaded by %s", fields.module, jid.bare(data.from));
739 local info = (#ok_list > 0 and ("The module "..fields.module.." was successfully unloaded on the hosts:\n"..t_concat(ok_list, "\n")) or "") 749 local info = (#ok_list > 0 and ("The module "..fields.module.." was successfully unloaded on the hosts:\n"..t_concat(ok_list, "\n")) or "")
740 .. ((#ok_list > 0 and #err_list > 0) and "\n" or "") .. 750 .. ((#ok_list > 0 and #err_list > 0) and "\n" or "") ..
741 (#err_list > 0 and ("Failed to unload the module "..fields.module.." on the hosts:\n"..t_concat(err_list, "\n")) or ""); 751 (#err_list > 0 and ("Failed to unload the module "..fields.module.." on the hosts:\n"..t_concat(err_list, "\n")) or "");
742 return { status = "completed", info = info }; 752 return { status = "completed", info = info };
743 end); 753 end);
749 759
750 { name = "FORM_TYPE", type = "hidden", value = "http://prosody.im/protocol/hosts#activate" }; 760 { name = "FORM_TYPE", type = "hidden", value = "http://prosody.im/protocol/hosts#activate" };
751 { name = "host", type = "text-single", required = true, label = "Host:"}; 761 { name = "host", type = "text-single", required = true, label = "Host:"};
752 }; 762 };
753 763
754 local activate_host_handler = adhoc_simple(activate_host_layout, function(fields, err) 764 local activate_host_handler = adhoc_simple(activate_host_layout, function(fields, err, data)
755 if err then 765 if err then
756 return generate_error_message(err); 766 return generate_error_message(err);
757 end 767 end
758 local ok, err = hostmanager_activate(fields.host); 768 local ok, err = hostmanager_activate(fields.host);
759 769
760 if ok then 770 if ok then
771 module:log("info", "Host '%s' activated by %s", fields.host, jid.bare(data.from));
761 return { status = "completed", info = fields.host .. " activated" }; 772 return { status = "completed", info = fields.host .. " activated" };
762 else 773 else
763 return { status = "canceled", error = err } 774 return { status = "canceled", error = err }
764 end 775 end
765 end); 776 end);
771 782
772 { name = "FORM_TYPE", type = "hidden", value = "http://prosody.im/protocol/hosts#activate" }; 783 { name = "FORM_TYPE", type = "hidden", value = "http://prosody.im/protocol/hosts#activate" };
773 { name = "host", type = "text-single", required = true, label = "Host:"}; 784 { name = "host", type = "text-single", required = true, label = "Host:"};
774 }; 785 };
775 786
776 local deactivate_host_handler = adhoc_simple(deactivate_host_layout, function(fields, err) 787 local deactivate_host_handler = adhoc_simple(deactivate_host_layout, function(fields, err, data)
777 if err then 788 if err then
778 return generate_error_message(err); 789 return generate_error_message(err);
779 end 790 end
780 local ok, err = hostmanager_deactivate(fields.host); 791 local ok, err = hostmanager_deactivate(fields.host);
781 792
782 if ok then 793 if ok then
794 module:log("info", "Host '%s' deactivated by %s", fields.host, jid.bare(data.from));
783 return { status = "completed", info = fields.host .. " deactivated" }; 795 return { status = "completed", info = fields.host .. " deactivated" };
784 else 796 else
785 return { status = "canceled", error = err } 797 return { status = "canceled", error = err }
786 end 798 end
787 end); 799 end);