Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Query string fixes #4

Merged
merged 6 commits into from
Oct 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions script/acceptance_tests/server.cr
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,34 @@ get "/crest-client" do |env|
"Hi, %s! You sent a request that was successfully verified with HMAC auth for the crest-client" % env.kemal_authorized_client?
end

post "/submit" do |env|
"Hi, %s! I got your POST request" % env.kemal_authorized_client?
end

# catch-all routes for all other requests and methods
# ["GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"]
get "/catch-all" do |env|
"Hi, %s! Welcome to catch-all" % env.kemal_authorized_client?
end

post "/catch-all" do |env|
"Hi, %s! Welcome to catch-all" % env.kemal_authorized_client?
end

put "/catch-all" do |env|
"Hi, %s! Welcome to catch-all" % env.kemal_authorized_client?
end

patch "/catch-all" do |env|
"Hi, %s! Welcome to catch-all" % env.kemal_authorized_client?
end

delete "/catch-all" do |env|
"Hi, %s! Welcome to catch-all" % env.kemal_authorized_client?
end

options "/catch-all" do |env|
"Hi, %s! Welcome to catch-all" % env.kemal_authorized_client?
end

Kemal.run
104 changes: 103 additions & 1 deletion script/acceptance_tests/tests.cr
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,32 @@ require "crest"
require "http/client"
require "spec"

describe "All HTTP Methods" do
["GET", "POST", "PUT", "PATCH", "DELETE", "OPTIONS"].each do |method|
it "successfully validates a #{method} request" do
client = Kemal::Hmac::Client.new("my_standard_client", "my_secret_2")

path = "/catch-all"

headers = HTTP::Headers.new
client.generate_headers(path).each do |key, value|
headers.add(key, value)
end

response = HTTP::Client.get("http://localhost:3000#{path}", headers: headers)

request = HTTP::Request.new(
method,
"/api",
headers: headers,
)

response.status_code.should eq 200
response.body.should contain "Hi, my_standard_client!"
end
end
end

describe "crest" do
it "successfully sends a request with HMAC auth with the crest client to the crest-client endpoint" do
client = Kemal::Hmac::Client.new("my_crest_client", "my_secret")
Expand All @@ -18,10 +44,24 @@ describe "crest" do
response.body.should contain "Hi, my_crest_client!"
end

it "successfully sends a request with HMAC auth with the crest client to the crest-client endpoint with query string params" do
client = Kemal::Hmac::Client.new("my_crest_client", "my_secret")

path = "/crest-client"

response = Crest.get(
"http://localhost:3000#{path}",
headers: client.generate_headers(path)
)

response.status_code.should eq 200
response.body.should contain "Hi, my_crest_client!"
end

it "fails to send a request with HMAC auth with the crest client to the crest-client endpoint" do
client = Kemal::Hmac::Client.new("my_crest_client", "incorrect_secret")

path = "/crest-client"
path = "/crest-client?foo=bar&q=baz&operation=1/2"

begin
Crest.get(
Expand All @@ -33,6 +73,36 @@ describe "crest" do
ex.response.body.should contain "Unauthorized: HMAC token does not match"
end
end

it "fails to send a request with HMAC auth to an endpoint using the POST HTTP verb" do
client = Kemal::Hmac::Client.new("my_crest_client", "incorrect_secret")

path = "/submit"

begin
Crest.post(
"http://localhost:3000#{path}",
headers: client.generate_headers(path)
)
rescue ex : Crest::Unauthorized
ex.response.status_code.should eq 401
ex.response.body.should contain "Unauthorized: HMAC token does not match"
end
end

it "successfully sends a request with HMAC auth with the crest client an endpoint using the POST HTTP verb" do
client = Kemal::Hmac::Client.new("my_crest_client", "my_secret")

path = "/submit"

response = Crest.post(
"http://localhost:3000#{path}",
headers: client.generate_headers(path)
)

response.status_code.should eq 200
response.body.should contain "Hi, my_crest_client! I got your POST request"
end
end

describe "http/client" do
Expand All @@ -52,6 +122,38 @@ describe "http/client" do
response.body.should contain "Hi, my_standard_client!"
end

it "successfully sends a request with HMAC auth with the http-client to an endpoint using the POST HTTP verb" do
client = Kemal::Hmac::Client.new("my_standard_client", "my_secret_2")

path = "/submit"

headers = HTTP::Headers.new
client.generate_headers(path).each do |key, value|
headers.add(key, value)
end

response = HTTP::Client.post("http://localhost:3000#{path}", headers: headers)

response.status_code.should eq 200
response.body.should contain "Hi, my_standard_client! I got your POST request"
end

it "successfully sends a request with HMAC auth with the http-client to the http-client endpoint with query string params" do
client = Kemal::Hmac::Client.new("my_standard_client", "my_secret_2")

path = "/http-client?foo=bar&q=baz&operation=1/2"

headers = HTTP::Headers.new
client.generate_headers(path).each do |key, value|
headers.add(key, value)
end

response = HTTP::Client.get("http://localhost:3000#{path}", headers: headers)

response.status_code.should eq 200
response.body.should contain "Hi, my_standard_client!"
end

it "fails to send a request with HMAC auth with the http-client to the http-client endpoint" do
client = Kemal::Hmac::Client.new("my_standard_client", "bad_secret")

Expand Down
50 changes: 50 additions & 0 deletions spec/kemal-hmac/kemal-hmac_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,56 @@ describe "Kemal::Hmac" do
context.kemal_authorized_client?.should eq(client)
end

it "uses a custom handler and correct HMAC auth and the request flows through successfully with query string params" do
client = "valid-octo-client"
hmac_handler = SpecAuthHandler.new(
hmac_secrets: {client => ["octo-secret-blue", "octo-secret-green"]},
)

hmac_client = Kemal::Hmac::Client.new(client, "octo-secret-green", "SHA256")
headers = hmac_client.generate_headers("/api")

request = HTTP::Request.new(
"GET",
"/api?foo=bar&moon=star",
headers: HTTP::Headers{
"hmac-client" => headers["hmac-client"],
"hmac-timestamp" => headers["hmac-timestamp"],
"hmac-token" => headers["hmac-token"],
},
)

io, context = create_request_and_return_io_and_context(hmac_handler, request)
response = HTTP::Client::Response.from_io(io, decompress: false)
response.status_code.should eq 404
context.kemal_authorized_client?.should eq(client)
end

it "uses a custom handler and correct HMAC auth and the request flows through successfully with query string params incorrecty in the client" do
client = "valid-octo-client"
hmac_handler = SpecAuthHandler.new(
hmac_secrets: {client => ["octo-secret-blue", "octo-secret-green"]},
)

hmac_client = Kemal::Hmac::Client.new(client, "octo-secret-green", "SHA256")
headers = hmac_client.generate_headers("/api?cat=dog&moon=star&q=1")

request = HTTP::Request.new(
"GET",
"/api?foo=bar&moon=star",
headers: HTTP::Headers{
"hmac-client" => headers["hmac-client"],
"hmac-timestamp" => headers["hmac-timestamp"],
"hmac-token" => headers["hmac-token"],
},
)

io, context = create_request_and_return_io_and_context(hmac_handler, request)
response = HTTP::Client::Response.from_io(io, decompress: false)
response.status_code.should eq 404
context.kemal_authorized_client?.should eq(client)
end

it "uses a custom handler and explicit hmac algo successfully" do
client = "valid-octo-client"
hmac_handler = SpecAuthHandler.new(
Expand Down
2 changes: 1 addition & 1 deletion src/kemal-hmac/client.cr
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ module Kemal::Hmac
# :return: a Hash of the HMAC headers
def generate_headers(path : String) : Hash(String, String)
timestamp = Time::Format::ISO_8601_DATE_TIME.format(Time.utc)
hmac_token = Kemal::Hmac::Token.new(@client, path, timestamp, @algorithm).hexdigest(@secret)
hmac_token = Kemal::Hmac::Token.new(@client, path.split("?").first, timestamp, @algorithm).hexdigest(@secret)

return {
HMAC_CLIENT_HEADER => @client,
Expand Down