Module: MAuth::Signable

Included in:
Request, Response
Defined in:
lib/mauth/request_and_response.rb

Overview

module which composes a string to sign.

includer must provide

  • SIGNATURE_COMPONENTS OR SIGNATURE_COMPONENTS_V2 constant - array of keys to get from

  • #attributes_for_signing

  • #merge_headers (takes a Hash of headers; returns an instance of includer’s own class whose headers have been updated with the argument headers)

Instance Method Summary collapse

Instance Method Details

#attributes_for_signingObject



104
105
106
# File 'lib/mauth/request_and_response.rb', line 104

def attributes_for_signing
  @attributes_for_signing
end

#encode_query_string(q_string) ⇒ Object

sorts query string parameters by codepoint, uri encodes keys and values, and rejoins parameters into a query string



83
84
85
86
87
88
# File 'lib/mauth/request_and_response.rb', line 83

def encode_query_string(q_string)
  q_string.split('&').sort.map do |part|
    k, e, v = part.partition('=')
    uri_escape(k) + e + uri_escape(v)
  end.join('&')
end

#initialize(attributes_for_signing) ⇒ Object



100
101
102
# File 'lib/mauth/request_and_response.rb', line 100

def initialize(attributes_for_signing)
  @attributes_for_signing = attributes_for_signing
end

#string_to_sign_v1(more_attributes) ⇒ Object

the string to sign for V1 protocol will be (where LF is line feed character) for requests:

string_to_sign =
  http_verb + <LF> +
  resource_url_path (no host, port or query string; first "/" is included) + <LF> +
  request_body + <LF> +
  app_uuid + <LF> +
  current_seconds_since_epoch

for responses:

string_to_sign =
  status_code_string + <LF> +
  response_body_digest + <LF> +
  app_uuid + <LF> +
  current_seconds_since_epoch


31
32
33
34
35
36
37
38
39
# File 'lib/mauth/request_and_response.rb', line 31

def string_to_sign_v1(more_attributes)
  attributes_for_signing = self.attributes_for_signing.merge(more_attributes)
  missing_attributes = self.class::SIGNATURE_COMPONENTS.select { |key| !attributes_for_signing.key?(key) || attributes_for_signing[key].nil? }
  missing_attributes.delete(:body) # body may be omitted
  if missing_attributes.any?
    raise(UnableToSignError, "Missing required attributes to sign: #{missing_attributes.inspect}\non object to sign: #{inspect}")
  end
  self.class::SIGNATURE_COMPONENTS.map { |k| attributes_for_signing[k].to_s }.join("\n")
end

#string_to_sign_v2(override_attrs) ⇒ Object

the string to sign for V2 protocol will be (where LF is line feed character) for requests:

string_to_sign =
  http_verb + <LF> +
  resource_url_path (no host, port or query string; first "/" is included) + <LF> +
  request_body_digest + <LF> +
  app_uuid + <LF> +
  current_seconds_since_epoch + <LF> +
  encoded_query_params

for responses:

string_to_sign =
  status_code_string + <LF> +
  response_body_digest + <LF> +
  app_uuid + <LF> +
  current_seconds_since_epoch


57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/mauth/request_and_response.rb', line 57

def string_to_sign_v2(override_attrs)
  attrs_with_overrides = self.attributes_for_signing.merge(override_attrs)

  # memoization of body_digest to avoid hashing three times when we call
  # string_to_sign_v2 three times in client#signature_valid_v2!
  # note that if :body is nil we hash an empty string ("")
  attrs_with_overrides[:body_digest] ||= Digest::SHA512.hexdigest(attrs_with_overrides[:body].to_s)
  attrs_with_overrides[:encoded_query_params] = encode_query_string(attrs_with_overrides[:query_string].to_s)

  missing_attributes = self.class::SIGNATURE_COMPONENTS_V2.reject do |key|
    attrs_with_overrides.dig(key)
  end

  missing_attributes.delete(:body_digest) # body may be omitted
  missing_attributes.delete(:encoded_query_params) # query_string may be omitted
  if missing_attributes.any?
    raise(UnableToSignError, "Missing required attributes to sign: #{missing_attributes.inspect}\non object to sign: #{inspect}")
  end

  self.class::SIGNATURE_COMPONENTS_V2.map do |k|
    attrs_with_overrides[k].to_s.dup.force_encoding('UTF-8')
  end.join("\n")
end

#uri_escape(string) ⇒ Object

percent encodes special characters, preserving character encoding. encodes space as ‘%20’ does not encode A-Z, a-z, 0-9, hyphen ( - ), underscore ( _ ), period ( . ), or tilde ( ~ ) NOTE the CGI.escape spec changed in 2.5 to not escape tildes. we gsub tilde encoding back to tildes to account for older Rubies



96
97
98
# File 'lib/mauth/request_and_response.rb', line 96

def uri_escape(string)
  CGI.escape(string).gsub(/\+|%7E/, '+' => '%20', '%7E' => '~')
end