# HG changeset patch # User Matthew Wild # Date 1610887760 0 # Node ID ee313922b8d18e8c0ef8f2cb40613995e880c8ea # Parent 84e60c3d6e61ab93a0d839cec96fc10e42d8dfe6 mod_http_admin_api: HTTP API for managing users and invites diff -r 84e60c3d6e61 -r ee313922b8d1 mod_http_admin_api/mod_http_admin_api.lua --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mod_http_admin_api/mod_http_admin_api.lua Sun Jan 17 12:49:20 2021 +0000 @@ -0,0 +1,112 @@ +local json = require "util.json"; + +module:depends("http"); + +local invites = module:depends("invites"); +local tokens = module:depends("tokenauth"); + +local json_content_type = "application/json"; + +local www_authenticate_header = ("Bearer realm=%q"):format(module.host.."/"..module.name); + +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 + return false; + end + + if auth_type == "Bearer" then + local token_info = tokens.get_token_info(auth_data); + if not token_info or not token_info.session then + return false; + end + return token_info.session; + end + return nil; +end + +function check_auth(routes) + local function check_request_auth(event) + local session = check_credentials(event.request); + if not session then + event.response.headers.authorization = www_authenticate_header; + return false, 401; + elseif session.auth_scope ~= "prosody:scope:admin" then + return false, 403; + end + event.session = session; + return true; + end + + for route, handler in pairs(routes) do + routes[route] = function (event, ...) + local permit, code = check_request_auth(event); + if not permit then + return code; + end + return handler(event, ...); + end; + end + return routes; +end + +local function token_info_to_invite_info(token_info) + return { + id = token_info.token; + type = token_info.type; + inviter = token_info.inviter; + jid = token_info.jid; + landing_page = token_info.landing_page; + created_at = token_info.created_at; + expires = token_info.expires; + }; +end + +function list_invites(event) + local invites_list = {}; + for token, invite in invites.pending_account_invites() do --luacheck: ignore 213/token + table.insert(invites_list, token_info_to_invite_info(invite)); + end + table.sort(invites_list, function (a, b) + return a.created_at < b.created_at; + end); + + event.response.headers["Content-Type"] = json_content_type; + return json.encode(invites_list); +end + +function get_invite_by_id(event, invite_id) + local invite = invites.get_account_invite_info(invite_id); + if not invite then + return 404; + end + + event.response.headers["Content-Type"] = json_content_type; + return json.encode(token_info_to_invite_info(invite)); +end + +function create_invite(event) + local invite = invites.create_account(nil, { source = "admin_api/"..event.session.username }); + if not invite then + return 500; + end + + event.response.headers["Content-Type"] = json_content_type; + return json.encode(token_info_to_invite_info(invite)); +end + +function delete_invite(event, invite_id) --luacheck: ignore 212/event + if not invites.delete_account_invite(invite_id) then + return 404; + end + return 200; +end + +module:provides("http", { + route = check_auth { + ["GET /invites"] = list_invites; + ["GET /invites/*"] = get_invite_by_id; + ["PUT /invites"] = create_invite; + ["DELETE /invites/*"] = delete_invite; + }; +});