changeset 6329:5571c75a4fed

mod_http_admin_api: Unify error reporting around util.error
author Matthew Wild <mwild1@gmail.com>
date Wed, 01 Oct 2025 11:25:57 +0100
parents 187b957dc528
children 4e63151010bf
files mod_http_admin_api/mod_http_admin_api.lua
diffstat 1 files changed, 55 insertions(+), 35 deletions(-) [+]
line wrap: on
line diff
--- a/mod_http_admin_api/mod_http_admin_api.lua	Tue Sep 30 15:55:57 2025 +0100
+++ b/mod_http_admin_api/mod_http_admin_api.lua	Wed Oct 01 11:25:57 2025 +0100
@@ -32,6 +32,24 @@
 
 local deleted_users = module:open_store("accounts_cleanup");
 
+local errors = require "util.error".init(module.name, "https://prosody.im/protocol/errors", {
+	["feature-not-implemented"] = { "cancel", "feature-not-implemented", "Feature not implemented" };
+	["unknown-invite-type"] = { "cancel", "bad-request", "Unknown invitation type" };
+	["invite-creation-failed"] = { "cancel", "internal-server-error", "Failed to create invitation" };
+	["invalid-json"] = { "modify", "bad-request", "Invalid JSON submitted" };
+	["user-not-found"] = { "cancel", "item-not-found", "User not found", "user-not-found" };
+
+	-- From mod_groups_internal
+	["group-name-required"] = { "modify", "bad-request", "Group name required", "group-name-required" };
+	["group-not-found"] = { "cancel", "item-not-found", "Group not found", "group-not-found" };
+
+	-- Generic
+	["conflict"] = { "cancel", "conflict", "Resource already exists" };
+	["internal-server-error"] = { "wait", "internal-server-error", "Internal server error" };
+	["service-unavailable"] = { "cancel", "service-unavailable", "Unable to perform the requested action" };
+	["bad-request"] = { "cancel", "bad-request", "Invalid request" };
+});
+
 local function check_credentials(request)
 	local auth_type, auth_data = string.match(request.headers.authorization or "", "^(%S+)%s(.+)$");
 	if not (auth_type and auth_data) then
@@ -69,7 +87,7 @@
 			if not permit then
 				return code;
 			end
-			return handler(event, ...);
+			return errors.coerce(handler(event, ...));
 		end;
 	end
 	return routes;
@@ -166,10 +184,10 @@
 			note = options.note;
 		}, options.ttl);
 	else
-		return 400;
+		return nil, "unknown-invite-type";
 	end
 	if not invite then
-		return 500;
+		return nil, "invite-creation-failed";
 	end
 	event.response.headers["Content-Type"] = json_content_type;
 	return json.encode(token_info_to_invite_info(invite));
@@ -455,16 +473,16 @@
 	if not username then return; end
 
 	local current_user = get_user_info(username);
-	if not current_user then return 404; end
+	if not current_user then return nil, "user-not-found"; end
 
 	local request = event.request;
 	if request.headers.content_type ~= json_content_type
 	or (not request.body or #request.body == 0) then
-		return 400;
+		return nil, "invalid-json";
 	end
 	local new_user = json.decode(event.request.body);
 	if not new_user then
-		return 400;
+		return nil, "invalid-json";
 	end
 
 	local updated_attributes = set.new(array.collect(it.keys(new_user)));
@@ -474,8 +492,9 @@
 	end
 
 	if new_user.enabled ~= nil and new_user.enabled ~= current_user.enabled then
-		if not user_attribute_writers.enabled(username, new_user.enabled) then
-			return 500;
+		local ok, err = user_attribute_writers.enabled(username, new_user.enabled);
+		if not ok then
+			return nil, err;
 		end
 	end
 
@@ -514,17 +533,18 @@
 
 	if new_user.role then
 		if not usermanager.set_user_role then
-			return 500, "feature-not-implemented";
+			return nil, "feature-not-implemented";
 		end
-		if not usermanager.set_user_role(username, module.host, new_user.role) then
-			module:log("error", "failed to set role %s for %s", new_user.role, username);
-			return 500;
+		local role, role_err = usermanager.set_user_role(username, module.host, new_user.role);
+		if not role then
+			module:log("error", "failed to set role %s for %s: ", new_user.role, username, role_err);
+			return nil, role_err;
 		end
 	end
 
 	if new_user.roles then -- COMPAT w/0.12
 		if not usermanager.set_user_roles then
-			return 500, "feature-not-implemented"
+			return nil, "feature-not-implemented";
 		end
 
 		local backend_roles = {};
@@ -532,15 +552,17 @@
 			backend_roles[role] = true;
 		end
 		local user_jid = username.."@"..module.host;
-		if not usermanager.set_user_roles(username, module.host, backend_roles) then
-			module:log("error", "failed to set roles %q for %s", backend_roles, user_jid)
-			return 500
+		local role, role_err = usermanager.set_user_roles(username, module.host, backend_roles);
+		if not role then
+			module:log("error", "failed to set roles %q for %s: %s", backend_roles, user_jid, role_err)
+			return nil, role_err;
 		end
 	end
 
 	if new_user.enabled ~= nil then
-		if not user_attribute_writers.enabled(username, new_user.enabled) then
-			return 500;
+		local ok, err = user_attribute_writers.enabled(username, new_user.enabled);
+		if not ok then
+			return nil, err;
 		end
 	end
 
@@ -548,8 +570,9 @@
 end
 
 function delete_user(event, username) --luacheck: ignore 212/event
-	if not usermanager.delete_user(username, module.host) then
-		return 404;
+	local ok, err = usermanager.delete_user(username, module.host);
+	if not ok then
+		return nil, err;
 	end
 	return 200;
 end
@@ -597,21 +620,16 @@
 		return 400;
 	end
 
-	if not group.name then
-		module:log("warn", "Group missing name property");
-		return 400;
-	end
-
 	local create_muc = group.create_muc and true or false;
 
-	local group_id = mod_groups.create(
+	local group_id, err = mod_groups.create(
 		{
 			name = group.name;
 		},
 		create_muc
 	);
 	if not group_id then
-		return 500;
+		return nil, err;
 	end
 
 	event.response.headers["Content-Type"] = json_content_type;
@@ -630,36 +648,38 @@
 	do
 		local group_id, member_name = group:match("^([^/]+)/members/([^/]+)$");
 		if group_id and member_name then
-			if not mod_groups.add_member(group_id, member_name) then
-				return 500;
+			local ok, err = mod_groups.add_member(group_id, member_name);
+			if not ok then
+				return nil, err;
 			end
 			return 204;
 		end
 	end
 
 	local group_id = group:match("^([^/]+)$")
-	if not group_id then return 404; end
+	if not group_id then return nil, "group-not-found"; end
 
 	local request = event.request;
 	if request.headers.content_type ~= json_content_type or (not request.body or #request.body == 0) then
-		return 400;
+		return nil, "invalid-json";
 	end
 
 	local update = json.decode(event.request.body);
 	if not update then
-		return 400;
+		return nil, "invalid-json";
 	end
 
 	local group_info = mod_groups.get_info(group_id);
 	if not group_info then
-		return 404;
+		return nil, "group-not-found";
 	end
 
 	if update.name then
 		group_info["name"] = update.name;
 	end
-	if not mod_groups.set_info(group_id, group_info) then
-		return 500;
+	local ok, err = mod_groups.set_info(group_id, group_info);
+	if not ok then
+		return nil, err;
 	end
 	return 204;
 end