Mercurial > prosody-hg
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 |
| parents | 21a1b9fb08a1 |
| children | 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); |
