Skip to content

Commit

Permalink
OPS: Fixed current time initialization in url signing
Browse files Browse the repository at this point in the history
co-authored-by: Krisztian Nagy <krisztian.nagy@emarsys.com>
  • Loading branch information
mihdavid and knagy committed Oct 18, 2019
1 parent f5d7b80 commit c277645
Show file tree
Hide file tree
Showing 4 changed files with 23 additions and 25 deletions.
36 changes: 20 additions & 16 deletions lib/escher/auth.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,17 @@ def initialize(credential_scope, options = {})


def sign!(req, client, headers_to_sign = [])
current_time = @current_time || Time.now

headers_to_sign |= [@date_header_name.downcase, 'host']

request = wrap_request req
raise EscherError, 'The host header is missing' unless request.has_header? 'host'

request.set_header(@date_header_name.downcase, format_date_for_header) unless request.has_header? @date_header_name
request.set_header(@date_header_name.downcase, format_date_for_header(current_time)) unless request.has_header? @date_header_name

signature = generate_signature(client[:api_secret], request.body, request.headers, request.method, headers_to_sign, request.path, request.query_values)
request.set_header(@auth_header_name, "#{@algo_id} Credential=#{client[:api_key_id]}/#{short_date(@current_time)}/#{@credential_scope}, SignedHeaders=#{prepare_headers_to_sign headers_to_sign}, Signature=#{signature}")
signature = generate_signature(client[:api_secret], request.body, request.headers, request.method, headers_to_sign, request.path, request.query_values, current_time)
request.set_header(@auth_header_name, "#{@algo_id} Credential=#{client[:api_key_id]}/#{short_date(current_time)}/#{@credential_scope}, SignedHeaders=#{prepare_headers_to_sign headers_to_sign}, Signature=#{signature}")

request.request
end
Expand All @@ -44,6 +46,7 @@ def is_valid?(*args)


def authenticate(req, key_db, mandatory_signed_headers = nil)
current_time = @current_time || Time.now
request = wrap_request req
method = request.method
body = request.body
Expand Down Expand Up @@ -80,7 +83,7 @@ def authenticate(req, key_db, mandatory_signed_headers = nil)
raise EscherError, 'The request method is invalid' unless valid_request_method?(method)
raise EscherError, "The request url shouldn't contains http or https" if path.match /^https?:\/\//
raise EscherError, 'Invalid date in authorization header, it should equal with date header' unless short_date(date) == short_date
raise EscherError, 'The request date is not within the accepted time range' unless is_date_within_range?(date, expires, @current_time || Time.now)
raise EscherError, 'The request date is not within the accepted time range' unless is_date_within_range?(date, expires, current_time)
raise EscherError, 'Invalid Credential Scope' unless credential_scope == @credential_scope
raise EscherError, 'The mandatorySignedHeaders parameter must be undefined or array of strings' unless mandatory_signed_headers_valid?(mandatory_signed_headers)
raise EscherError, 'The host header is not signed' unless signed_headers.include? 'host'
Expand All @@ -93,7 +96,7 @@ def authenticate(req, key_db, mandatory_signed_headers = nil)
raise EscherError, 'The date header is not signed' if !signature_from_query && !signed_headers.include?(@date_header_name.downcase)

escher = reconfig(algorithm, credential_scope, date)
expected_signature = escher.generate_signature(api_secret, body, headers, method, signed_headers, path, query_parts)
expected_signature = escher.generate_signature(api_secret, body, headers, method, signed_headers, path, query_parts, date)
raise EscherError, 'The signatures do not match' unless signature == expected_signature
api_key_id
end
Expand All @@ -115,6 +118,7 @@ def reconfig(algorithm, credential_scope, date)


def generate_signed_url(url_to_sign, client, expires = 86400)
current_time = @current_time || Time.now
uri = Addressable::URI.parse(url_to_sign)

if (not uri.port.nil?) && (uri.port != uri.default_port)
Expand All @@ -136,13 +140,13 @@ def generate_signed_url(url_to_sign, client, expires = 86400)
body = 'UNSIGNED-PAYLOAD'
query_parts += [
['Algorithm', @algo_id],
['Credentials', "#{client[:api_key_id]}/#{short_date(@current_time)}/#{@credential_scope}"],
['Date', long_date(@current_time)],
['Credentials', "#{client[:api_key_id]}/#{short_date(current_time)}/#{@credential_scope}"],
['Date', long_date(current_time)],
['Expires', expires.to_s],
['SignedHeaders', headers_to_sign.join(';')],
].map { |k, v| query_pair(k, v) }

signature = generate_signature(client[:api_secret], body, headers, 'GET', headers_to_sign, path, query_parts)
signature = generate_signature(client[:api_secret], body, headers, 'GET', headers_to_sign, path, query_parts, current_time)
query_parts_with_signature = (query_parts.map { |k, v| [uri_encode(k), uri_encode(v)] } << query_pair('Signature', signature))
"#{uri.scheme}://#{host}#{path}?#{query_parts_with_signature.map { |k, v| k + '=' + v }.join('&')}#{(fragment === nil ? '' : '#' + fragment)}"
end
Expand Down Expand Up @@ -188,11 +192,11 @@ def get_auth_parts_from_query(query_parts)



def generate_signature(api_secret, body, headers, method, signed_headers, path, query_parts)
def generate_signature(api_secret, body, headers, method, signed_headers, path, query_parts, current_time)
canonicalized_request = canonicalize(method, path, query_parts, body, headers, signed_headers.uniq)
string_to_sign = get_string_to_sign(canonicalized_request)
string_to_sign = get_string_to_sign(canonicalized_request, current_time)

signing_key = OpenSSL::HMAC.digest(@algo, @algo_prefix + api_secret, short_date(@current_time))
signing_key = OpenSSL::HMAC.digest(@algo, @algo_prefix + api_secret, short_date(current_time))
@credential_scope.split('/').each { |data|
signing_key = OpenSSL::HMAC.digest(@algo, signing_key, data)
}
Expand All @@ -202,8 +206,8 @@ def generate_signature(api_secret, body, headers, method, signed_headers, path,



def format_date_for_header
@date_header_name.downcase == 'date' ? @current_time.utc.rfc2822.sub('-0000', 'GMT') : long_date(@current_time)
def format_date_for_header(current_time)
@date_header_name.downcase == 'date' ? current_time.utc.rfc2822.sub('-0000', 'GMT') : long_date(current_time)
end


Expand Down Expand Up @@ -238,11 +242,11 @@ def parse_uri(request_uri)



def get_string_to_sign(canonicalized_request)
def get_string_to_sign(canonicalized_request, current_time)
[
@algo_id,
long_date(@current_time),
short_date(@current_time) + '/' + @credential_scope,
long_date(current_time),
short_date(current_time) + '/' + @credential_scope,
@algo.new.hexdigest(canonicalized_request)
].join("\n")
end
Expand Down
2 changes: 1 addition & 1 deletion lib/escher/version.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module Escher
VERSION = '1.0.2'
VERSION = '2.0.0'
end
6 changes: 0 additions & 6 deletions spec/emarsys_test_suite_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,5 @@ module Escher
expect(request).to eq(test_case.expected_request)
end
end


xspecify "every case in the test suite is being used" do
expect(::EmarsysTestSuiteHelpers::TestSuite.in_use_size).to eq ::EmarsysTestSuiteHelpers::TestSuite.size
end

end
end
4 changes: 2 additions & 2 deletions spec/escher/auth_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ def client
headers_to_sign = headers.map { |k| k[0].downcase }
path, query_parts = escher.parse_uri(request_uri)
canonicalized_request = escher.canonicalize(method, path, query_parts, body, headers, headers_to_sign)
string_to_sign = escher.get_string_to_sign(canonicalized_request)
string_to_sign = escher.get_string_to_sign(canonicalized_request, Time.parse(date))
expect(string_to_sign).to eq(fixture(suite, test, 'sts'))
end
end
Expand Down Expand Up @@ -484,7 +484,7 @@ def client

it 'should convert dates' do
date_str = 'Fri, 09 Sep 2011 23:36:00 GMT'
expect(described_class.new('irrelevant', date_header_name: 'date', current_time: Time.parse(date_str)).format_date_for_header).to eq date_str
expect(described_class.new('irrelevant', date_header_name: 'date', current_time: Time.parse(date_str)).format_date_for_header(Time.parse(date_str))).to eq date_str
end


Expand Down

0 comments on commit c277645

Please sign in to comment.