Class: Signature::Request
- Inherits:
-
Object
- Object
- Signature::Request
- Includes:
- QueryEncoder
- Defined in:
- lib/signature/request.rb
Constant Summary collapse
- AUTH_HEADER_PREFIX =
"X-API-"
- AUTH_HEADER_PREFIX_REGEX =
/X\-API\-(AUTH\-.+)$|X_API_(AUTH_.+)$/
- ISO8601 =
"%Y-%m-%dT%H:%M:%SZ"
Instance Attribute Summary collapse
-
#path ⇒ Object
Returns the value of attribute path.
-
#query_hash ⇒ Object
Returns the value of attribute query_hash.
Class Method Summary collapse
Instance Method Summary collapse
-
#auth_hash ⇒ Object
Expose the authentication parameters for a signed request.
-
#authenticate(timestamp_grace = 600) ⇒ Object
Authenticate a request.
-
#authenticate_async(timestamp_grace = 600) ⇒ Object
Authenticate a request asynchronously.
-
#authenticate_by_token(token, timestamp_grace = 600) ⇒ Object
Authenticate the request with a token, but rather than raising an exception if the request is invalid, simply returns false.
-
#authenticate_by_token!(token, timestamp_grace = 600) ⇒ Object
Authenticates the request with a token.
-
#initialize(method, path, query, headers = nil) ⇒ Request
constructor
A new instance of Request.
-
#sign(token) ⇒ Object
Sign the request with the given token, and return the computed authentication parameters.
-
#signed_params ⇒ Object
Query parameters merged with the computed authentication parameters.
Methods included from QueryEncoder
encode_param, encode_param_without_escaping
Constructor Details
#initialize(method, path, query, headers = nil) ⇒ Request
Returns a new instance of Request.
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
# File 'lib/signature/request.rb', line 16 def initialize(method, path, query, headers=nil) raise ArgumentError, "Expected string" unless path.kind_of?(String) raise ArgumentError, "Expected hash" unless query.kind_of?(Hash) query_hash = {} auth_hash = self.class.parse_headers(headers) if headers auth_hash ||= {} query.each do |key, v| k = key.to_s.downcase k[0..4] == 'auth_' ? auth_hash[k] = v : query_hash[k] = v end @method = method.upcase @path, @query_hash, @auth_hash = path, query_hash, auth_hash @signed = false end |
Instance Attribute Details
#path ⇒ Object
Returns the value of attribute path.
9 10 11 |
# File 'lib/signature/request.rb', line 9 def path @path end |
#query_hash ⇒ Object
Returns the value of attribute query_hash.
9 10 11 |
# File 'lib/signature/request.rb', line 9 def query_hash @query_hash end |
Class Method Details
.parse_headers(headers = {}) ⇒ Object
35 36 37 38 39 40 41 42 43 44 45 |
# File 'lib/signature/request.rb', line 35 def self.parse_headers headers={} hh = {} headers.each do |k,v| if match = k.upcase.match(AUTH_HEADER_PREFIX_REGEX) mindex = match[1] if match[1] mindex ||= match[2] hh[mindex.downcase.gsub('-', '_')] = v if mindex end end hh end |
Instance Method Details
#auth_hash ⇒ Object
Expose the authentication parameters for a signed request
161 162 163 164 |
# File 'lib/signature/request.rb', line 161 def auth_hash raise "Request not signed" unless @signed @auth_hash end |
#authenticate(timestamp_grace = 600) ⇒ Object
Authenticate a request
Takes a block which will be called with the auth_key from the request, and which should return a Signature::Token (or nil if no token can be found for the key)
Raises errors in the same way as authenticate_by_token!
108 109 110 111 112 113 114 115 116 117 118 |
# File 'lib/signature/request.rb', line 108 def authenticate( = 600) raise ArgumentError, "Block required" unless block_given? key = @auth_hash['auth_key'] raise AuthenticationError, "Missing parameter: auth_key" unless key token = yield key unless token raise AuthenticationError, "Unknown auth_key" end authenticate_by_token!(token, ) return token end |
#authenticate_async(timestamp_grace = 600) ⇒ Object
Authenticate a request asynchronously
This method is useful it you’re running a server inside eventmachine and need to lookup the token asynchronously.
The block is passed an auth key and a deferrable which should succeed with the token, or fail if the token cannot be found
This method returns a deferrable which succeeds with the valid token, or fails with an AuthenticationError which can be used to pass the error back to the user
132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 |
# File 'lib/signature/request.rb', line 132 def authenticate_async( = 600) raise ArgumentError, "Block required" unless block_given? df = EM::DefaultDeferrable.new key = @auth_hash['auth_key'] unless key df.fail(AuthenticationError.new("Missing parameter: auth_key")) return end token_df = yield key token_df.callback { |token| begin authenticate_by_token!(token, ) df.succeed(token) rescue AuthenticationError => e df.fail(e) end } token_df.errback { df.fail(AuthenticationError.new("Unknown auth_key")) } ensure return df end |
#authenticate_by_token(token, timestamp_grace = 600) ⇒ Object
Authenticate the request with a token, but rather than raising an exception if the request is invalid, simply returns false
94 95 96 97 98 |
# File 'lib/signature/request.rb', line 94 def authenticate_by_token(token, = 600) authenticate_by_token!(token, ) rescue AuthenticationError false end |
#authenticate_by_token!(token, timestamp_grace = 600) ⇒ Object
Authenticates the request with a token
Raises an AuthenticationError if the request is invalid. AuthenticationError exception messages are designed to be exposed to API consumers, and should help them correct errors generating signatures
Timestamp: Unless timestamp_grace is set to nil (which allows this check to be skipped), AuthenticationError will be raised if the timestamp is missing or further than timestamp_grace period away from the real time (defaults to 10 minutes)
Signature: Raises AuthenticationError if the signature does not match the computed HMAC. The error contains a hint for how to sign.
77 78 79 80 81 82 83 84 85 86 87 88 89 |
# File 'lib/signature/request.rb', line 77 def authenticate_by_token!(token, = 600) # Validate that your code has provided a valid token. This does not # raise an AuthenticationError since passing tokens with empty secret is # a code error which should be fixed, not reported to the API's consumer if token.secret.nil? || token.secret.empty? raise AuthenticationError, "Provided token is missing secret" end validate_version! () validate_signature!(token) true end |
#sign(token) ⇒ Object
Sign the request with the given token, and return the computed authentication parameters
50 51 52 53 54 55 56 57 58 59 60 61 |
# File 'lib/signature/request.rb', line 50 def sign(token) @auth_hash = { :auth_version => "1.0", :auth_key => token.key, :auth_timestamp => Time.now.to_i.to_s } @auth_hash[:auth_signature] = signature(token) @signed = true return @auth_hash end |
#signed_params ⇒ Object
Query parameters merged with the computed authentication parameters
168 169 170 |
# File 'lib/signature/request.rb', line 168 def signed_params @query_hash.merge(auth_hash) end |