Module: ApiSignature::Utils

Defined in:
lib/api_signature/utils.rb

Class Method Summary collapse

Class Method Details

.canonical_header_value(value) ⇒ Object



84
85
86
# 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) ⇒ Object



92
93
94
# 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) ⇒ Object



88
89
90
# 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) ⇒ Object



96
97
98
99
100
# 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) ⇒ Object



61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# 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) ⇒ Object



113
114
115
116
117
118
# 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) ⇒ Object

constant-time comparison algorithm to prevent timing attacks



103
104
105
106
107
108
109
110
111
# 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) ⇒ String<SHA256 Hexdigest>

Parameters:

  • value (File, Tempfile, IO#read, String)

Returns:

  • (String<SHA256 Hexdigest>)


13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
# 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) ⇒ true/false

Parameters:

  • uri (URI)

Returns:

  • (true/false)


33
34
35
36
# 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) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



53
54
55
56
57
58
59
# 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) ⇒ Object



48
49
50
# 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) ⇒ Object



38
39
40
41
42
43
44
45
46
# 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