Class: Miasma::Contrib::AwsApiCore::SignatureV4

Inherits:
Signature
  • Object
show all
Defined in:
lib/miasma/contrib/aws.rb

Overview

AWS signature version 4

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods inherited from Signature

#safe_escape

Constructor Details

#initialize(access_key, secret_key, region, service) ⇒ self

Create new signature generator

Parameters:

  • access_key (String)
  • secret_key (String)
  • region (String)
  • service (String)


152
153
154
155
156
157
# File 'lib/miasma/contrib/aws.rb', line 152

def initialize(access_key, secret_key, region, service)
  @hmac = Hmac.new('sha256', secret_key)
  @access_key = access_key
  @region = region
  @service = service
end

Instance Attribute Details

#access_keyString (readonly)

Returns access key.

Returns:

  • (String)

    access key



139
140
141
# File 'lib/miasma/contrib/aws.rb', line 139

def access_key
  @access_key
end

#hmacHmac (readonly)

Returns:



137
138
139
# File 'lib/miasma/contrib/aws.rb', line 137

def hmac
  @hmac
end

#regionString (readonly)

Returns region.

Returns:

  • (String)

    region



141
142
143
# File 'lib/miasma/contrib/aws.rb', line 141

def region
  @region
end

#serviceString (readonly)

Returns service.

Returns:

  • (String)

    service



143
144
145
# File 'lib/miasma/contrib/aws.rb', line 143

def service
  @service
end

Instance Method Details

#algorithmString

Returns signature algorithm.

Returns:

  • (String)

    signature algorithm



229
230
231
# File 'lib/miasma/contrib/aws.rb', line 229

def algorithm
  'AWS4-HMAC-SHA256'
end

#build_canonical_request(http_method, path, opts) ⇒ String

Build the canonical request string used for signing

Parameters:

  • http_method (Symbol)

    HTTP request method

  • path (String)

    request path

  • opts (Hash)

    request options

Returns:

  • (String)

    canonical request string



257
258
259
260
261
262
263
264
265
266
267
268
269
# File 'lib/miasma/contrib/aws.rb', line 257

def build_canonical_request(http_method, path, opts)
  unless(path.start_with?('/'))
    path = "/#{path}"
  end
  [
    http_method.to_s.upcase,
    path,
    canonical_query(opts[:params]),
    canonical_headers(opts[:headers]),
    signed_headers(opts[:headers]),
    canonical_payload(opts)
  ].join("\n")
end

#canonical_headers(headers) ⇒ String

Build the canonical header string used for signing

Parameters:

  • headers (Hash)

    request headers

Returns:

  • (String)

    canonical headers string



287
288
289
290
291
292
293
# File 'lib/miasma/contrib/aws.rb', line 287

def canonical_headers(headers)
  headers ||= {}
  headers = Hash[headers.sort_by(&:first)]
  headers.map do |key, value|
    [key.downcase, value.chomp].join(':')
  end.join("\n") << "\n"
end

#canonical_payload(options) ⇒ String

Build the canonical payload string used for signing

Parameters:

  • options (Hash)

    request options

Returns:

  • (String)

    body checksum



309
310
311
312
313
314
315
316
317
318
319
320
321
# File 'lib/miasma/contrib/aws.rb', line 309

def canonical_payload(options)
  body = options.fetch(:body, '')
  if(options[:json])
    body = MultiJson.dump(options[:json])
  elsif(options[:form])
    body = URI.encode_www_form(options[:form])
  end
  if(body == 'UNSIGNED-PAYLOAD')
    body
  else
    hmac.hexdigest_of(body)
  end
end

#canonical_query(params) ⇒ String

Build the canonical query string used for signing

Parameters:

  • params (Hash)

    query params

Returns:

  • (String)

    canonical query string



275
276
277
278
279
280
281
# File 'lib/miasma/contrib/aws.rb', line 275

def canonical_query(params)
  params ||= {}
  params = Hash[params.sort_by(&:first)]
  query = params.map do |key, value|
    "#{safe_escape(key)}=#{safe_escape(value)}"
  end.join('&')
end

#credential_scopeString

Returns credential scope for request.

Returns:

  • (String)

    credential scope for request



234
235
236
237
238
239
240
241
# File 'lib/miasma/contrib/aws.rb', line 234

def credential_scope
  [
    Time.now.utc.strftime('%Y%m%d'),
    region,
    service,
    'aws4_request'
  ].join('/')
end

#generate(http_method, path, opts) ⇒ String

Generate the signature string for AUTH

Parameters:

  • http_method (Symbol)

    HTTP request method

  • path (String)

    request path

  • opts (Hash)

    request options

Returns:

  • (String)

    signature



165
166
167
168
# File 'lib/miasma/contrib/aws.rb', line 165

def generate(http_method, path, opts)
  signature = generate_signature(http_method, path, opts)
  "#{algorithm} Credential=#{access_key}/#{credential_scope}, SignedHeaders=#{signed_headers(opts[:headers])}, Signature=#{signature}"
end

#generate_signature(http_method, path, opts) ⇒ String

Generate the signature

Parameters:

  • http_method (Symbol)

    HTTP request method

  • path (String)

    request path

  • opts (Hash)

    request options

Returns:

  • (String)

    signature



195
196
197
198
199
200
201
202
203
204
205
# File 'lib/miasma/contrib/aws.rb', line 195

def generate_signature(http_method, path, opts)
  to_sign = [
    algorithm,
    AwsApiCore.time_iso8601,
    credential_scope,
    hashed_canonical_request(
      can_req = build_canonical_request(http_method, path, opts)
    )
  ].join("\n")
  signature = sign_request(to_sign)
end

#generate_url(http_method, path, opts) ⇒ String

Generate URL with signed params

Parameters:

  • http_method (Symbol)

    HTTP request method

  • path (String)

    request path

  • opts (Hash)

    request options

Returns:

  • (String)

    signature



176
177
178
179
180
181
182
183
184
185
186
187
# File 'lib/miasma/contrib/aws.rb', line 176

def generate_url(http_method, path, opts)
  opts[:params].merge!(
    Smash.new(
      'X-Amz-SignedHeaders' => signed_headers(opts[:headers]),
      'X-Amz-Algorithm' => algorithm,
      'X-Amz-Credential' => "#{access_key}/#{credential_scope}"
    )
  )
  signature = generate_signature(http_method, path, opts.merge(:body => 'UNSIGNED-PAYLOAD'))
  params = opts[:params].merge('X-Amz-Signature' => signature)
  "https://#{opts[:headers]['Host']}/#{path}?#{canonical_query(params)}"
end

#hashed_canonical_request(request) ⇒ String

Generate the hash of the canonical request

Parameters:

  • request (String)

    canonical request string

Returns:

  • (String)

    hashed canonical request



247
248
249
# File 'lib/miasma/contrib/aws.rb', line 247

def hashed_canonical_request(request)
  hmac.hexdigest_of(request)
end

#sign_request(request) ⇒ String

Sign the request

Parameters:

  • request (String)

    request to sign

Returns:

  • (String)

    signature



211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
# File 'lib/miasma/contrib/aws.rb', line 211

def sign_request(request)
  key = hmac.sign(
    'aws4_request',
    hmac.sign(
      service,
      hmac.sign(
        region,
        hmac.sign(
          Time.now.utc.strftime('%Y%m%d'),
          "AWS4#{hmac.key}"
        )
      )
    )
  )
  hmac.hex_sign(request, key)
end

#signed_headers(headers) ⇒ String

List of headers included in signature

Parameters:

  • headers (Hash)

    request headers

Returns:

  • (String)

    header list



299
300
301
302
303
# File 'lib/miasma/contrib/aws.rb', line 299

def signed_headers(headers)
  headers ||= {}
  headers.sort_by(&:first).map(&:first).
    map(&:downcase).join(';')
end