Skip to content
Open
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
2 changes: 2 additions & 0 deletions app/controllers/admin/errors_controller.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
class Admin::ErrorsController < Admin::BaseController
skip_before_action :authenticate_user!

def bad_request
render(status: :bad_request)
end
Expand Down
9 changes: 7 additions & 2 deletions app/controllers/admin/link_checker_api_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,19 @@ def callback
private

def verify_signature
return unless webhook_secret_token
return head :service_unavailable unless webhook_secret_token

given_signature = request.headers["X-LinkCheckerApi-Signature"]
return head :bad_request unless given_signature

body = request.raw_post
signature = OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new("sha1"), webhook_secret_token, body)
head :bad_request unless Rack::Utils.secure_compare(signature, given_signature)
return if Rack::Utils.secure_compare(signature, given_signature)

# Opt out of gds-sso's Warden intercept_401, which would otherwise turn
# this response into a redirect to /auth/gds.
request.env["warden"].custom_failure!
head :unauthorized
end

def webhook_secret_token
Expand Down
11 changes: 11 additions & 0 deletions test/functional/admin/errors_controller_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,17 @@ class Admin::ErrorsControllerTest < ActionDispatch::IntegrationTest
assert_template error
end

it "should render the #{error} page when not authenticated" do
logout
ENV["GDS_SSO_MOCK_INVALID"] = "true"

get "/#{error_code}"

assert_template error
ensure
ENV.delete("GDS_SSO_MOCK_INVALID")
end

it "should render the correct headers" do
get "/#{error_code}"

Expand Down
19 changes: 19 additions & 0 deletions test/functional/admin/link_checker_api_controller_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,4 +37,23 @@ def generate_signature(body, key)

assert_response :success
end

test "POST :callback returns 503 when the webhook secret is not configured" do
Rails.application.credentials.stubs(:link_checker_api_secret_token).returns(nil)
LinkCheckerApiReport.any_instance.expects(:mark_report_as_completed).never

body = link_checker_api_batch_report_hash(id: 5, links: [{ uri: @link, status: "ok" }])
post :callback, params: body

assert_response :service_unavailable
end

test "POST :callback returns 400 when the signature header is missing" do
LinkCheckerApiReport.any_instance.expects(:mark_report_as_completed).never

body = link_checker_api_batch_report_hash(id: 5, links: [{ uri: @link, status: "ok" }])
post :callback, params: body

assert_response :bad_request
end
end
22 changes: 22 additions & 0 deletions test/integration/admin/link_checker_api_callback_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
require "test_helper"

class Admin::LinkCheckerApiCallbackTest < ActionDispatch::IntegrationTest
test "returns 401 (not a redirect) when the signature is invalid" do
post admin_link_checker_api_callback_path,
params: { id: 5 }.to_json,
headers: {
"Content-Type" => "application/json",
"X-LinkCheckerApi-Signature" => "invalid",
}

assert_response :unauthorized
end

test "returns 400 when the signature header is missing" do
post admin_link_checker_api_callback_path,
params: { id: 5 }.to_json,
headers: { "Content-Type" => "application/json" }

assert_response :bad_request
end
end