# HG changeset patch # User tmolitor # Date 1583693989 -3600 # Node ID a6b3b41a116c13f081f57b8e4c9184962d6ac659 # Parent 3f4df08dce147866d8f94602e54fb5a654522466# Parent 469408682152456e86fa9ebd344751af21e7280e Merge commit diff -r 3f4df08dce14 -r a6b3b41a116c mod_http_oauth2/mod_http_oauth2.lua --- a/mod_http_oauth2/mod_http_oauth2.lua Sun Mar 08 19:53:52 2020 +0100 +++ b/mod_http_oauth2/mod_http_oauth2.lua Sun Mar 08 19:59:49 2020 +0100 @@ -67,6 +67,7 @@ end function handle_token_grant(event) + event.response.headers.content_type = "application/json"; local params = http.formdecode(event.request.body); if not params then return oauth_error("invalid_request"); diff -r 3f4df08dce14 -r a6b3b41a116c mod_rest/jsonmap.lib.lua --- a/mod_rest/jsonmap.lib.lua Sun Mar 08 19:53:52 2020 +0100 +++ b/mod_rest/jsonmap.lib.lua Sun Mar 08 19:59:49 2020 +0100 @@ -340,6 +340,7 @@ form:text_tag("value", v_); end end + form:up(); end return form; end diff -r 3f4df08dce14 -r a6b3b41a116c mod_rest/mod_rest.lua --- a/mod_rest/mod_rest.lua Sun Mar 08 19:53:52 2020 +0100 +++ b/mod_rest/mod_rest.lua Sun Mar 08 19:59:49 2020 +0100 @@ -83,14 +83,7 @@ return nil, "unknown-payload-type"; end -local supported_types = { - "application/xmpp+xml", - "application/json", - "application/x-www-form-urlencoded", - "text/plain", -}; - -local function decide_type(accept) +local function decide_type(accept, supported_types) -- assumes the accept header is sorted local ret = supported_types[1]; for i = 2, #supported_types do @@ -101,6 +94,18 @@ return ret; end +local supported_inputs = { + "application/xmpp+xml", + "application/json", + "application/x-www-form-urlencoded", + "text/plain", +}; + +local supported_outputs = { + "application/xmpp+xml", + "application/json", +}; + local function encode(type, s) if type == "application/json" then return json.encode(jsonmap.st2json(s)); @@ -110,6 +115,17 @@ return tostring(s); end +local post_errors = { + parse = { code = 400, condition = "not-well-formed", text = "Failed to parse payload", }, + xmlns = { code = 422, condition = "invalid-namespace", text = "'xmlns' attribute must be empty", }, + name = { code = 422, condition = "unsupported-stanza-type", text = "Invalid stanza, must be 'message', 'presence' or 'iq'.", }, + to = { code = 422, condition = "improper-addressing", text = "Invalid destination JID", }, + from = { code = 422, condition = "invalid-from", text = "Invalid source JID", }, + post_auth = { code = 403, condition = "not-authorized", text = "Not authorized to send stanza with requested 'from'", }, + iq_type = { code = 422, condition = "invalid-xml", text = "'iq' stanza must be of type 'get' or 'set'", }, + iq_tags = { code = 422, condition = "bad-format", text = "'iq' stanza must have exactly one child tag", }, +}; + local function handle_post(event) local request, response = event.request, event.response; local from; @@ -128,26 +144,26 @@ local payload, err = parse(request.headers.content_type, request.body); if not payload then -- parse fail - return errors.new({ code = 400, text = "Failed to parse payload" }, { error = err, type = request.headers.content_type, data = request.body }); + return errors.new("parse", { error = err, type = request.headers.content_type, data = request.body, }, post_errors); end if payload.attr.xmlns then - return errors.new({ code = 422, text = "'xmlns' attribute must be empty" }); + return errors.new("xmlns", nil, post_errors); elseif payload.name ~= "message" and payload.name ~= "presence" and payload.name ~= "iq" then - return errors.new({ code = 422, text = "Invalid stanza, must be 'message', 'presence' or 'iq'." }); + return errors.new("name", nil, post_errors); end local to = jid.prep(payload.attr.to); if not to then - return errors.new({ code = 422, text = "Invalid destination JID" }); + return errors.new("to", nil, post_errors); end if payload.attr.from then local requested_from = jid.prep(payload.attr.from); if not requested_from then - return errors.new({ code = 422, text = "Invalid source JID" }); + return errors.new("from", nil, post_errors); end if jid.compare(requested_from, from) then from = requested_from; else - return errors.new({ code = 403, text = "Not authorized to send from "..requested_from }); + return errors.new("from_auth", nil, post_errors); end end payload.attr = { @@ -158,15 +174,15 @@ ["xml:lang"] = payload.attr["xml:lang"], }; module:log("debug", "Received[rest]: %s", payload:top_tag()); - local send_type = decide_type((request.headers.accept or "") ..",".. request.headers.content_type) + local send_type = decide_type((request.headers.accept or "") ..",".. request.headers.content_type, supported_outputs) if payload.name == "iq" then function origin.send(stanza) module:send(stanza); end if payload.attr.type ~= "get" and payload.attr.type ~= "set" then - return errors.new({ code = 422, text = "'iq' stanza must be of type 'get' or 'set'" }); + return errors.new("iq_type", nil, post_errors); elseif #payload.tags ~= 1 then - return errors.new({ code = 422, text = "'iq' stanza must have exactly one child tag" }); + return errors.new("iq_tags", nil, post_errors); end return module:send_iq(payload, origin):next( function (result) @@ -224,7 +240,7 @@ module:set_status("info", "Connected"); end if code == 200 and response.headers.accept then - send_type = decide_type(response.headers.accept); + send_type = decide_type(response.headers.accept, supported_outputs); module:log("debug", "Set 'rest_callback_content_type' = %q based on Accept header", send_type); end end); @@ -280,7 +296,7 @@ headers = { ["Content-Type"] = send_type, ["Content-Language"] = stanza.attr["xml:lang"], - Accept = table.concat(supported_types, ", "); + Accept = table.concat(supported_inputs, ", "); }, }, function (body, code, response) if code == 0 then @@ -376,10 +392,29 @@ end end +local supported_errors = { + "text/html", + "application/xmpp+xml", + "application/json", +}; + local http_server = require "net.http.server"; module:hook_object_event(http_server, "http-error", function (event) local request, response = event.request, event.response; - if decide_type(request and request.headers.accept or "") == "application/json" then + local response_as = decide_type(request and request.headers.accept or "", supported_errors); + if response_as == "application/xmpp+xml" then + if response then + response.headers.content_type = "application/xmpp+xml"; + end + local stream_error = st.stanza("error", { xmlns = "http://etherx.jabber.org/streams" }); + if event.error then + stream_error:tag(event.error.condition, {xmlns = 'urn:ietf:params:xml:ns:xmpp-streams' }):up(); + if event.error.text then + stream_error:text_tag("text", event.error.text, {xmlns = 'urn:ietf:params:xml:ns:xmpp-streams' }); + end + end + return tostring(stream_error); + elseif response_as == "application/json" then if response then response.headers.content_type = "application/json"; end @@ -389,4 +424,4 @@ code = event.code, }); end -end, 10); +end, 1);