Mercurial > prosody-modules
comparison mod_protect_last_admin/mod_protect_last_admin.lua @ 6326:2623515d8507
mod_protect_last_admin: New module to ensure the last admin account on a server cannot be demoted or removed
| author | Matthew Wild <mwild1@gmail.com> |
|---|---|
| date | Thu, 25 Sep 2025 18:21:27 +0100 |
| parents | |
| children | 4f6fb56572f7 |
comparison
equal
deleted
inserted
replaced
| 6325:ba2c344e1bdd | 6326:2623515d8507 |
|---|---|
| 1 module:set_global(); | |
| 2 | |
| 3 local um = require "prosody.core.usermanager"; | |
| 4 | |
| 5 local set = require "prosody.util.set"; | |
| 6 | |
| 7 local protected_roles = module:get_option_set("protect_last_admin_roles", { "prosody:admin", "prosody:operator" }); | |
| 8 | |
| 9 local function other_protected_users(username, host) | |
| 10 local protected_users = set.new(); | |
| 11 for protected_role in protected_roles do | |
| 12 local users = um.get_users_with_role(protected_role, host); | |
| 13 module:log("debug", "Role %s: %s", protected_role, require "util.serialization".serialize(users, "debug")); | |
| 14 protected_users:add_list(um.get_users_with_role(protected_role, host)); | |
| 15 end | |
| 16 protected_users:remove(username); | |
| 17 return not protected_users:empty(); | |
| 18 end | |
| 19 | |
| 20 function check_last_admin(event) | |
| 21 local username, host = event.username, event.host; | |
| 22 | |
| 23 local current_role = um.get_user_role(username, host); | |
| 24 if not current_role then | |
| 25 module:log("warn", "Couldn't detect role for %s@%s", username, host); | |
| 26 return; | |
| 27 end | |
| 28 | |
| 29 module:log("debug", "Checking whether to allow this event..."); | |
| 30 | |
| 31 if event.role_name and protected_roles:contains(event.role_name) then | |
| 32 module:log("debug", "Permitting role change to %s", event.role_name); | |
| 33 return; -- Switching to another protected role is fine | |
| 34 end | |
| 35 | |
| 36 if not protected_roles:contains(current_role.name) then | |
| 37 module:log("debug", "Permitting event, role %s is not protected", current_role.name); | |
| 38 return; -- This is not a protected user | |
| 39 end | |
| 40 | |
| 41 if not other_protected_users(username, host) then | |
| 42 event.reason = "Cannot remove the only administrator"; | |
| 43 return false; | |
| 44 end | |
| 45 module:log("debug", "Permitting event, no reason not to!"); | |
| 46 end | |
| 47 | |
| 48 module:hook("pre-delete-user", check_last_admin); | |
| 49 module:hook("pre-disable-user", check_last_admin); | |
| 50 module:hook("pre-user-role-change", check_last_admin); |
