Commit 10b76ad
Changed files (2)
lib
scim
kit
spec
scim
kit
lib/scim/kit/http.rb
@@ -9,6 +9,8 @@ module Scim
end
end
+ MAX_REDIRECTS = 3
+
attr_reader :driver, :retries
def initialize(driver: Http.default_driver, retries: 3)
@@ -23,7 +25,7 @@ module Scim
def fetch(uri, headers: {})
driver.with_retry(retries: retries) do |client|
- response = client.get(uri, headers: headers)
+ response = get_following_redirects(client, uri, headers)
Result.new(response.code.to_i, parse(response.body))
end
rescue *Net::Hippie::CONNECTION_ERRORS => error
@@ -33,7 +35,7 @@ module Scim
def self.default_driver
@default_driver ||= Net::Hippie::Client.new(
- follow_redirects: 3,
+ follow_redirects: 0,
headers: headers,
logger: Scim::Kit.logger,
open_timeout: 1,
@@ -51,6 +53,30 @@ module Scim
private
+ # net-hippie rebuilds the redirected request without the per-request
+ # headers, so follow redirects here to keep them.
+ def get_following_redirects(client, uri, headers, limit: MAX_REDIRECTS)
+ uri = URI.parse(uri.to_s)
+ response = client.get(uri, headers: headers)
+ location = response['location'] if response.is_a?(Net::HTTPRedirection)
+ return response if limit.zero? || location.to_s.empty?
+
+ target = uri.merge(location)
+ get_following_redirects(
+ client, target, forwardable(headers, uri, target), limit: limit - 1
+ )
+ end
+
+ def forwardable(headers, from, to)
+ return headers if origin(from) == origin(to)
+
+ headers.reject { |name, _| name.to_s.casecmp?('authorization') }
+ end
+
+ def origin(uri)
+ [uri.scheme, uri.host, uri.port]
+ end
+
def parse(body)
return {} if body.nil?
spec/scim/kit/http_spec.rb
@@ -59,5 +59,37 @@ RSpec.describe Scim::Kit::Http do
specify { expect(subject.fetch(uri, headers: { 'X-Test' => 'value' })).to be_ok }
end
+
+ context 'when the response redirects to the same origin' do
+ let(:credentials) { { 'Authorization' => 'Bearer xyz' } }
+ let(:redirect_uri) { URI.join(uri, '/v2/Users') }
+
+ before do
+ stub_request(:get, uri)
+ .to_return(status: 301, headers: { 'Location' => redirect_uri.to_s })
+ stub_request(:get, redirect_uri)
+ .with(headers: credentials).to_return(status: 200, body: '{}')
+ end
+
+ specify { expect(subject.fetch(uri, headers: credentials)).to be_ok }
+ end
+
+ context 'when the response redirects to another origin' do
+ let(:credentials) { { 'Authorization' => 'Bearer xyz' } }
+ let(:redirect_uri) { URI('https://elsewhere.example.com/Users') }
+
+ before do
+ stub_request(:get, uri)
+ .to_return(status: 301, headers: { 'Location' => redirect_uri.to_s })
+ stub_request(:get, redirect_uri).to_return(status: 200, body: '{}')
+ end
+
+ it 'does not forward the credentials' do
+ subject.fetch(uri, headers: credentials)
+
+ expect(a_request(:get, redirect_uri)
+ .with(headers: credentials)).not_to have_been_made
+ end
+ end
end
end