diff --git a/lib/api_auth/headers.rb b/lib/api_auth/headers.rb index bb0f4c6c..b8c04a6c 100644 --- a/lib/api_auth/headers.rb +++ b/lib/api_auth/headers.rb @@ -94,9 +94,31 @@ def sign_header(header) def parse_uri(uri) parsed_uri = URI.parse(uri) - return parsed_uri.request_uri if parsed_uri.respond_to?(:request_uri) + uri_without_host = parsed_uri.respond_to?(:request_uri) ? parsed_uri.request_uri : uri + return '/' if uri_without_host.empty? + escape_params(uri_without_host) + end - uri.empty? ? '/' : uri + # Different versions of request parsers escape/unescape the param values + # Examples: + # Rails 5.1.3 ApiAuth canonical_string: + # 'GET,application/json,,/api/v1/employees?select=epulse_id%2Cfirst_name%2Clast_name,Thu, 14 Dec 2017 16:19:48 GMT' + # Rails 5.1.4 ApiAuth canonical_string: + # 'GET,application/json,,/api/v1/employees?select=epulse_id,first_name,last_name,Thu, 14 Dec 2017 16:20:57 GMT' + # This will force param values to escaped and fixes issue #123 + def escape_params(uri) + unescaped_uri = CGI.unescape(uri) + uri_array = unescaped_uri.split('?') + return uri unless uri_array.length > 1 + params = uri_array[1].split('&') + encoded_params = '' + params.each do |param| + next unless param.include?('=') + encoded_params += '&' unless encoded_params.empty? + split_param = param.split('=') + encoded_params += split_param[0] + '=' + CGI.escape(split_param[1]) + end + uri_array[0] + '?' + encoded_params end end end diff --git a/spec/headers_spec.rb b/spec/headers_spec.rb index fe31b794..36dea91d 100644 --- a/spec/headers_spec.rb +++ b/spec/headers_spec.rb @@ -35,13 +35,39 @@ let(:uri) { 'http://google.com/?redirect_to=https://www.example.com'.freeze } it 'return /?redirect_to=https://www.example.com as canonical string path' do - expect(subject.canonical_string).to eq('GET,,,/?redirect_to=https://www.example.com,') + expect(subject.canonical_string).to eq('GET,,,/?redirect_to=https%3A%2F%2Fwww.example.com,') end it 'does not change request url (by removing host)' do expect(request.url).to eq(uri) end end + + if RUBY_VERSION.to_f > 2.1 + context 'uri param values are not escaped' do + let(:uri) do + 'http://www.google.com/search/advanced?redirect_to=https://www.example.com&account=a12dd334/3444\:23'.freeze + end + + it 'returns correct anonical string' do + expect(subject.canonical_string).to( + eq('GET,,,/search/advanced?redirect_to=https%3A%2F%2Fwww.example.com&account=a12dd334%2F3444%5C%3A23,') + ) + end + end + end + + context 'uri param values are escaped' do + let(:uri) do + 'http://www.google.com/search/advanced?redirect_to=https%3A%2F%2Fwww.example.com&account=a12dd334%2F3444%5C%3A23'.freeze + end + + it 'returns correct anonical string' do + expect(subject.canonical_string).to( + eq('GET,,,/search/advanced?redirect_to=https%3A%2F%2Fwww.example.com&account=a12dd334%2F3444%5C%3A23,') + ) + end + end end context 'string construction' do