Skip to content

Commit

Permalink
HTTP API: be able to upload definitions
Browse files Browse the repository at this point in the history
This route is mostly used by the mgmt UI (where it works), but it is
also exposed as part of the HTTP API, so let's make it work without
Referer too.

Only added a small sanity check to the test cases, as the implementation
is the same as for the JSON API (and it will probably stay that way).

Addresses this crash:

    2020-10-28 15:26:41 +01:00 [ERROR] httpserver: method=POST path=/api/definitions/upload status=500 error=Missing hash key: HTTP::Headers::Key(@name="Referer") (KeyError)
      from ../../../../usr/local/Cellar/crystal/0.35.1_1/src/hash.cr:1030:9 in '[]'
      from ../../../../usr/local/Cellar/crystal/0.35.1_1/src/http/headers.cr:71:5 in '[]'
      from src/avalanchemq/http/controller.cr:101:9 in 'redirect_back'
      from src/avalanchemq/http/controller/definitions.cr:32:11 in '->'
      from ../../../../usr/local/Cellar/crystal/0.35.1_1/src/primitives.cr:255:3 in 'call'
      from ../../../../usr/local/Cellar/crystal/0.35.1_1/src/http/server/handler.cr:28:7 in 'call_next'
      from lib/router/src/router/handler/handler.cr:25:9 in 'call'
      from ../../../../usr/local/Cellar/crystal/0.35.1_1/src/http/server/handler.cr:28:7 in 'call_next'
      from src/avalanchemq/http/handler/basic_auth.cr:31:26 in 'call'
      from ../../../../usr/local/Cellar/crystal/0.35.1_1/src/http/server/handler.cr:28:7 in 'call_next'
      from src/avalanchemq/http/controller/static.cr:20:11 in 'call'
      from ../../../../usr/local/Cellar/crystal/0.35.1_1/src/http/server/handler.cr:28:7 in 'call_next'
      from src/avalanchemq/http/handler/error_handler.cr:12:9 in 'call'
      from ../../../../usr/local/Cellar/crystal/0.35.1_1/src/http/server/handler.cr:28:7 in 'call_next'
      from src/avalanchemq/http/handler/defaults_handler.cr:13:9 in 'call'
      from ../../../../usr/local/Cellar/crystal/0.35.1_1/src/http/server/handler.cr:28:7 in 'call_next'
      from lib/http-protection/src/http-protection/frame_options.cr:23:7 in 'call'
      from ../../../../usr/local/Cellar/crystal/0.35.1_1/src/http/server/handler.cr:28:7 in 'call_next'
      from lib/http-protection/src/http-protection/strict_transport.cr:24:7 in 'call'
      from ../../../../usr/local/Cellar/crystal/0.35.1_1/src/http/server/request_processor.cr:50:11 in 'process'
      from ../../../../usr/local/Cellar/crystal/0.35.1_1/src/http/server.cr:513:5 in 'handle_client'
      from ../../../../usr/local/Cellar/crystal/0.35.1_1/src/http/server.cr:468:13 in '->'
      from ../../../../usr/local/Cellar/crystal/0.35.1_1/src/primitives.cr:255:3 in 'run'
      from ../../../../usr/local/Cellar/crystal/0.35.1_1/src/fiber.cr:92:34 in '->'
  • Loading branch information
dentarg authored and carlhoerberg committed Oct 29, 2020
1 parent 57a4981 commit 0950953
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 5 deletions.
6 changes: 2 additions & 4 deletions openapi/paths/definitions.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -70,10 +70,8 @@
schema:
"$ref": "../openapi.yaml#/components/schemas/definitions-UploadDefinitionsRequestBody"
responses:
# FIXME: should be 200 OK when used outside the UI
# to address "Operation must have at least one `2xx` response." warning
'302':
description: Found.
'200':
description: OK
4XX:
description: Client Error
content:
Expand Down
39 changes: 39 additions & 0 deletions spec/api/definitions_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -424,4 +424,43 @@ describe AvalancheMQ::HTTP::Server do
response.status_code.should eq 200
end
end

describe "POST /api/definitions/upload" do
it "imports definitions from uploaded file (no Referer)" do
file_content = %({ "vhosts":[{ "name":"uploaded_vhost" }] }) # sanity check
io = IO::Memory.new
builder = HTTP::FormData::Builder.new(io)
builder.file("file", IO::Memory.new(file_content))
builder.finish

headers = {"Content-Type" => builder.content_type}
body = io.to_s

response = post("/api/definitions/upload", headers: headers, body: body)
response.status_code.should eq 200
s.vhosts["uploaded_vhost"]?.should_not be_nil
s.vhosts["uploaded_vhost"].should be_a(AvalancheMQ::VHost)
ensure
s.vhosts.delete("uploaded_vhost")
end

it "imports definitions from uploaded file" do
file_content = %({ "vhosts":[{ "name":"uploaded_vhost" }] }) # sanity check
io = IO::Memory.new
builder = HTTP::FormData::Builder.new(io)
builder.file("file", IO::Memory.new(file_content))
builder.finish

headers = {"Content-Type" => builder.content_type, "Referer" => "/foo"}
body = io.to_s

response = post("/api/definitions/upload", headers: headers, body: body)
response.status_code.should eq 302
response.headers["Location"].should eq "/foo"
s.vhosts["uploaded_vhost"]?.should_not be_nil
s.vhosts["uploaded_vhost"].should be_a(AvalancheMQ::VHost)
ensure
s.vhosts.delete("uploaded_vhost")
end
end
end
3 changes: 2 additions & 1 deletion src/avalanchemq/http/controller/definitions.cr
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ module AvalancheMQ
import_definitions(body)
end
end
redirect_back(context)
redirect_back(context) if context.request.headers["Referer"]?
context
end

get "/api/definitions/:vhost" do |context, params|
Expand Down

0 comments on commit 0950953

Please sign in to comment.