Software /
code /
prosody
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); |