From c277645cace95118957714b068865b3f1260c699 Mon Sep 17 00:00:00 2001 From: mihalek david Date: Fri, 18 Oct 2019 15:55:08 +0200 Subject: [PATCH] OPS: Fixed current time initialization in url signing co-authored-by: Krisztian Nagy --- lib/escher/auth.rb | 36 ++++++++++++++++++--------------- lib/escher/version.rb | 2 +- spec/emarsys_test_suite_spec.rb | 6 ------ spec/escher/auth_spec.rb | 4 ++-- 4 files changed, 23 insertions(+), 25 deletions(-) diff --git a/lib/escher/auth.rb b/lib/escher/auth.rb index d065c4d..3ab0fad 100644 --- a/lib/escher/auth.rb +++ b/lib/escher/auth.rb @@ -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 @@ -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 @@ -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' @@ -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 @@ -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) @@ -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 @@ -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) } @@ -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 @@ -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 diff --git a/lib/escher/version.rb b/lib/escher/version.rb index 5c5b8bd..ce3ec36 100644 --- a/lib/escher/version.rb +++ b/lib/escher/version.rb @@ -1,3 +1,3 @@ module Escher - VERSION = '1.0.2' + VERSION = '2.0.0' end diff --git a/spec/emarsys_test_suite_spec.rb b/spec/emarsys_test_suite_spec.rb index 29f2544..78959af 100644 --- a/spec/emarsys_test_suite_spec.rb +++ b/spec/emarsys_test_suite_spec.rb @@ -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 diff --git a/spec/escher/auth_spec.rb b/spec/escher/auth_spec.rb index 0408b4a..edc3654 100644 --- a/spec/escher/auth_spec.rb +++ b/spec/escher/auth_spec.rb @@ -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 @@ -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