Module: OpenStax::Api::Params
Constant Summary collapse
- RESERVED_CHARACTERS =
Below is borrowed from github.com/oauth-xx/oauth-ruby/blob/e397b3e2f540faaebd7912aeb2768835d151f795/lib/oauth/helper.rb so we can call ‘normalize` on some params without adding dependence on full oauth gem
/[^a-zA-Z0-9\-\.\_\~]/
Instance Method Summary collapse
- #_escape(string) ⇒ Object
-
#escape(value) ⇒ Object
Escape
value
by URL encoding all non-reserved character. -
#normalize(params) ⇒ Object
Normalize a
Hash
of parameter values. -
#normalize_nested_query(value, prefix = nil) ⇒ Object
Returns a string representation of the Hash like in URL query string build_nested_query(=> {:level_2 => [‘value_1’,‘value_2’]}, ‘prefix’)) #=> [“prefix%5Blevel_1%5D%5Blevel_2%5D%5B%5D=value_1”, “prefix%5Blevel_1%5D%5Blevel_2%5D%5B%5D=value_2”].
- #sign(params:, secret:, algorithm: 'sha256') ⇒ Object
- #signature_and_timestamp_valid?(params:, secret:, algorithm: 'sha256', timestamp_window_width: 2.minutes) ⇒ Boolean
Instance Method Details
#_escape(string) ⇒ Object
48 49 50 |
# File 'lib/openstax/api/params.rb', line 48 def _escape(string) Addressable::URI.escape_component(string, RESERVED_CHARACTERS) end |
#escape(value) ⇒ Object
Escape value
by URL encoding all non-reserved character.
42 43 44 45 46 |
# File 'lib/openstax/api/params.rb', line 42 def escape(value) _escape(value.to_s.to_str) rescue ArgumentError _escape(value.to_s.to_str.force_encoding(Encoding::UTF_8)) end |
#normalize(params) ⇒ Object
Normalize a Hash
of parameter values. Parameters are sorted by name, using lexicographical byte value ordering. If two or more parameters share the same name, they are sorted by their value. Parameters are concatenated in their sorted order into a single string. For each parameter, the name is separated from the corresponding value by an “=” character, even if the value is empty. Each name-value pair is separated by an “&” character.
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 |
# File 'lib/openstax/api/params.rb', line 57 def normalize(params) params.sort.map do |k, values| if values.is_a?(Array) # make sure the array has an element so we don't lose the key values << nil if values.empty? # multiple values were provided for a single key values.sort.collect do |v| [escape(k),escape(v)] * "=" end elsif values.is_a?(Hash) normalize_nested_query(values, k) else [escape(k),escape(values)] * "=" end end * "&" end |
#normalize_nested_query(value, prefix = nil) ⇒ Object
Returns a string representation of the Hash like in URL query string build_nested_query(=> {:level_2 => [‘value_1’,‘value_2’]}, ‘prefix’))
#=> ["prefix%5Blevel_1%5D%5Blevel_2%5D%5B%5D=value_1", "prefix%5Blevel_1%5D%5Blevel_2%5D%5B%5D=value_2"]
77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
# File 'lib/openstax/api/params.rb', line 77 def normalize_nested_query(value, prefix = nil) case value when Array value.map do |v| normalize_nested_query(v, "#{prefix}[]") end.flatten.sort when Hash value.map do |k, v| normalize_nested_query(v, prefix ? "#{prefix}[#{k}]" : k) end.flatten.sort else [escape(prefix), escape(value)] * "=" end end |
#sign(params:, secret:, algorithm: 'sha256') ⇒ Object
9 10 11 12 13 14 15 16 17 |
# File 'lib/openstax/api/params.rb', line 9 def sign(params:, secret:, algorithm: 'sha256') raise "`secret` cannot be blank" if secret.blank? local_params = params.merge(timestamp: Time.now.to_i) stringified_params = normalize(local_params) signature = OpenSSL::HMAC.hexdigest(algorithm, secret, stringified_params) local_params.merge!(signature: signature) end |
#signature_and_timestamp_valid?(params:, secret:, algorithm: 'sha256', timestamp_window_width: 2.minutes) ⇒ Boolean
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
# File 'lib/openstax/api/params.rb', line 19 def (params:, secret:, algorithm: 'sha256', timestamp_window_width: 2.minutes) local_params = params.dup incoming_signature = local_params.delete(:signature) return false if incoming_signature.blank? stringified_params = normalize(local_params) expected_signature = OpenSSL::HMAC.hexdigest(algorithm, secret, stringified_params) return false if expected_signature != incoming_signature = .ago...from_now return false if !.cover?(Time.at(params[:timestamp].to_i)) return true end |