module ApiSignature::Utils
Public Class Methods
canonical_header_value(value)
click to toggle source
# File lib/api_signature/utils.rb, line 84 def self.canonical_header_value(value) value.match(/^".*"$/) ? value : value.gsub(/\s+/, ' ').strip end
hexhmac(key, value)
click to toggle source
# File lib/api_signature/utils.rb, line 92 def self.hexhmac(key, value) OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'), key, value) end
hmac(key, value)
click to toggle source
# File lib/api_signature/utils.rb, line 88 def self.hmac(key, value) OpenSSL::HMAC.digest(OpenSSL::Digest.new('sha256'), key, value) end
normalize_keys(hash)
click to toggle source
# File lib/api_signature/utils.rb, line 96 def self.normalize_keys(hash) return {} unless hash hash.transform_keys { |key| key.to_s.downcase } end
normalized_querystring(querystring)
click to toggle source
# File lib/api_signature/utils.rb, line 61 def self.normalized_querystring(querystring) return unless querystring params = querystring.split('&') params = params.map { |p| p.match(/=/) ? p : p + '=' } # We have to sort by param name and preserve order of params that # have the same name. Default sort <=> in JRuby will swap members # occasionally when <=> is 0 (considered still sorted), but this # causes our normalized query string to not match the sent querystring. # When names match, we then sort by their original order params.each.with_index.sort do |a, b| a, a_offset = a a_name = a.split('=')[0] b, b_offset = b b_name = b.split('=')[0] if a_name == b_name a_offset <=> b_offset else a_name <=> b_name end end.map(&:first).join('&') end
safe_parse_datetime(value, format = nil)
click to toggle source
# File lib/api_signature/utils.rb, line 113 def self.safe_parse_datetime(value, format = nil) format ||= ApiSignature.configuration.datetime_format DateTime.strptime(value, format) rescue ArgumentError => _e nil end
secure_compare(string_a, string_b)
click to toggle source
constant-time comparison algorithm to prevent timing attacks
# File lib/api_signature/utils.rb, line 103 def self.secure_compare(string_a, string_b) return false if string_a.nil? || string_b.nil? || string_a.bytesize != string_b.bytesize l = string_a.unpack "C#{string_a.bytesize}" res = 0 string_b.each_byte { |byte| res |= byte ^ l.shift } res == 0 end
sha256_hexdigest(value)
click to toggle source
@param [File, Tempfile, IO#read, String] value @return [String<SHA256 Hexdigest>]
# File lib/api_signature/utils.rb, line 13 def self.sha256_hexdigest(value) if (File === value || Tempfile === value) && !value.path.nil? && File.exist?(value.path) OpenSSL::Digest::SHA256.file(value).hexdigest elsif value.respond_to?(:read) sha256 = OpenSSL::Digest::SHA256.new while chunk = value.read(1024 * 1024, buffer ||= '') # 1MB sha256.update(chunk) end value.rewind sha256.hexdigest else OpenSSL::Digest::SHA256.hexdigest(value) end end
standard_port?(uri)
click to toggle source
@param [URI] uri @return [true/false]
# File lib/api_signature/utils.rb, line 33 def self.standard_port?(uri) (uri.scheme == 'http' && uri.port == 80) || (uri.scheme == 'https' && uri.port == 443) end
uri_escape(string)
click to toggle source
@api private
# File lib/api_signature/utils.rb, line 53 def self.uri_escape(string) if string.nil? nil else CGI.escape(string.encode('UTF-8')).gsub('+', '%20').gsub('%7E', '~') end end
uri_escape_path(path)
click to toggle source
# File lib/api_signature/utils.rb, line 48 def self.uri_escape_path(path) path.gsub(/[^\/]+/) { |part| uri_escape(part) } end
url_path(path, uri_escape_path = false)
click to toggle source
# File lib/api_signature/utils.rb, line 38 def self.url_path(path, uri_escape_path = false) path = '/' if path == '' if uri_escape_path uri_escape_path(path) else path end end