Module: MAuth::Signable
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
- #attributes_for_signing ⇒ Object
- #initialize(attributes_for_signing) ⇒ Object
-
#normalize_path(path) ⇒ Object
Addressable::URI.parse(path).normalize.to_s.squeeze(‘/’).
-
#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.
-
#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.
-
#unescape_encode_query_string(q_string) ⇒ Object
sorts query string parameters by codepoint, uri encodes keys and values, and rejoins parameters into a query string.
-
#uri_escape(string) ⇒ Object
percent encodes special characters, preserving character encoding.
Instance Method Details
#attributes_for_signing ⇒ Object
128 129 130 |
# File 'lib/mauth/request_and_response.rb', line 128 def attributes_for_signing @attributes_for_signing end |
#initialize(attributes_for_signing) ⇒ Object
124 125 126 |
# File 'lib/mauth/request_and_response.rb', line 124 def initialize(attributes_for_signing) @attributes_for_signing = attributes_for_signing end |
#normalize_path(path) ⇒ Object
Addressable::URI.parse(path).normalize.to_s.squeeze(‘/’)
92 93 94 95 96 97 98 99 100 101 |
# File 'lib/mauth/request_and_response.rb', line 92 def normalize_path(path) return if path.nil? # Addressable::URI.normalize_path normalizes `.` and `..` in path # i.e. /./example => /example ; /example/.. => / # String#squeeze removes duplicated slahes i.e. /// => / # String#gsub normalizes percent encoding to uppercase i.e. %cf%80 => %CF%80 Addressable::URI.normalize_path(path).squeeze('/') .gsub(/%[a-f0-9]{2}/, &:upcase) 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 + <LF> +
app_uuid + <LF> +
current_seconds_since_epoch
34 35 36 37 38 39 40 41 42 43 44 45 46 |
# File 'lib/mauth/request_and_response.rb', line 34 def string_to_sign_v1(more_attributes) attributes_for_signing = self.attributes_for_signing.merge(more_attributes) missing_attributes = self.class::SIGNATURE_COMPONENTS.select do |key| !attributes_for_signing.key?(key) || attributes_for_signing[key].nil? end 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
64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 |
# File 'lib/mauth/request_and_response.rb', line 64 def string_to_sign_v2(override_attrs) attrs_with_overrides = 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] ||= OpenSSL::Digest.hexdigest('SHA512', attrs_with_overrides[:body] || '') attrs_with_overrides[:encoded_query_params] = unescape_encode_query_string(attrs_with_overrides[:query_string] || '') attrs_with_overrides[:request_url] = normalize_path(attrs_with_overrides[:request_url]) missing_attributes = self.class::SIGNATURE_COMPONENTS_V2.reject do |key| attrs_with_overrides[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 |
#unescape_encode_query_string(q_string) ⇒ Object
sorts query string parameters by codepoint, uri encodes keys and values, and rejoins parameters into a query string
105 106 107 108 109 110 111 112 |
# File 'lib/mauth/request_and_response.rb', line 105 def unescape_encode_query_string(q_string) q_string.split('&').map do |part| k, _eq, v = part.partition('=') [CGI.unescape(k), CGI.unescape(v)] end.sort.map do |k, v| # rubocop:disable Style/MultilineBlockChain "#{uri_escape(k)}=#{uri_escape(v)}" end.join('&') 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
120 121 122 |
# File 'lib/mauth/request_and_response.rb', line 120 def uri_escape(string) CGI.escape(string).gsub(/\+|%7E/, '+' => '%20', '%7E' => '~') end |