Class: CrashLog::AuthHMAC

Inherits:
Object
  • Object
show all
Includes:
Headers
Defined in:
lib/crash_log/auth_hmac.rb,
lib/crash_log/auth_hmac/version.rb

Defined Under Namespace

Modules: Headers Classes: CanonicalString

Constant Summary collapse

VERSION =
"1.1.7"
@@default_signature_class =
CanonicalString

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Headers

#find_header, #headers

Constructor Details

#initialize(credential_store, options = nil) ⇒ AuthHMAC

Create an AuthHMAC instance using the given credential store

Credential Store:

  • Credential store must respond to the [] method and return a secret for access key id

Options: Override default options

  • :service_id - Service ID used in the AUTHORIZATION header string. Default is AuthHMAC.

  • :signature_method - Proc object that takes request and produces the signature string

    used for authentication. Default is CanonicalString.
    

Examples:

my_hmac = AuthHMAC.new('access_id1' => 'secret1', 'access_id2' => 'secret2')

cred_store = { 'access_id1' => 'secret1', 'access_id2' => 'secret2' }
options = { :service_id => 'MyApp', :signature_method => lambda { |r| MyRequestString.new(r) } }
my_hmac = AuthHMAC.new(cred_store, options)


145
146
147
148
149
150
151
152
153
154
155
156
157
158
# File 'lib/crash_log/auth_hmac.rb', line 145

def initialize(credential_store, options = nil)
  @credential_store = credential_store

  # Defaults
  @service_id = self.class.name.split(/::/).last
  @signature_class = @@default_signature_class

  unless options.nil?
    @service_id = options[:service_id] if options.key?(:service_id)
    @signature_class = options[:signature] if options.key?(:signature) && options[:signature].is_a?(Class)
  end

  @signature_method = lambda { |r| @signature_class.send(:new, r) }
end

Class Method Details

.authenticated?(request, access_key_id, secret, options) ⇒ Boolean

Authenticates a request using HMAC

Supports same options as AuthHMAC.initialize for overriding service_id and signature method.

Returns:

  • (Boolean)


193
194
195
196
# File 'lib/crash_log/auth_hmac.rb', line 193

def self.authenticated?(request, access_key_id, secret, options)
  credentials = { access_key_id => secret }
  self.new(credentials, options).authenticated?(request)
end

.canonical_string(request, options = nil) ⇒ Object

Generates canonical signing string for given request

Supports same options as AuthHMAC.initialize for overriding service_id and signature method.



165
166
167
# File 'lib/crash_log/auth_hmac.rb', line 165

def self.canonical_string(request, options = nil)
  self.new(nil, options).canonical_string(request)
end

.sign!(request, access_key_id, secret, options = nil) ⇒ Object

Signs a request using a given access key id and secret.

Supports same options as AuthHMAC.initialize for overriding service_id and signature method.



183
184
185
186
# File 'lib/crash_log/auth_hmac.rb', line 183

def self.sign!(request, access_key_id, secret, options = nil)
  credentials = { access_key_id => secret }
  self.new(credentials, options).sign!(request, access_key_id)
end

.signature(request, secret, options = nil) ⇒ Object

Generates signature string for a given secret

Supports same options as AuthHMAC.initialize for overriding service_id and signature method.



174
175
176
# File 'lib/crash_log/auth_hmac.rb', line 174

def self.signature(request, secret, options = nil)
  self.new(nil, options).signature(request, secret)
end

Instance Method Details

#authenticated?(request) ⇒ Boolean

Authenticates a request using HMAC

Returns true if the request has an AuthHMAC Authorization header and the access id and HMAC match an id and HMAC produced for the secret in the credential store. Otherwise returns false.

Returns:

  • (Boolean)


219
220
221
222
223
224
225
226
227
228
229
# File 'lib/crash_log/auth_hmac.rb', line 219

def authenticated?(request)
  rx = Regexp.new("#{@service_id} ([^:]+):(.+)$")
  if md = rx.match(authorization_header(request))
    access_key_id = md[1]
    hmac = md[2]
    secret = @credential_store[access_key_id]
    !secret.nil? && hmac == signature(request, secret)
  else
    false
  end
end

#authorization(request, access_key_id, secret) ⇒ Object



247
248
249
# File 'lib/crash_log/auth_hmac.rb', line 247

def authorization(request, access_key_id, secret)
  "#{@service_id} #{access_key_id}:#{signature(request, secret)}"
end

#authorization_header(request) ⇒ Object



243
244
245
# File 'lib/crash_log/auth_hmac.rb', line 243

def authorization_header(request)
  find_header(%w(Authorization HTTP_AUTHORIZATION), headers(request))
end

#canonical_string(request) ⇒ Object



239
240
241
# File 'lib/crash_log/auth_hmac.rb', line 239

def canonical_string(request)
  @signature_method.call(request)
end

#sign!(request, access_key_id) ⇒ Object

Signs a request using the access_key_id and the secret associated with that id in the credential store.

Signing a requests adds an Authorization header to the request in the format:

<service_id> <access_key_id>:<signature>

where <signature> is the Base64 encoded HMAC-SHA1 of the CanonicalString and the secret.

Raises:

  • (ArgumentError)


207
208
209
210
211
# File 'lib/crash_log/auth_hmac.rb', line 207

def sign!(request, access_key_id)
  secret = @credential_store[access_key_id]
  raise ArgumentError, "No secret found for key id '#{access_key_id}'" if secret.nil?
  request['Authorization'] = authorization(request, access_key_id, secret)
end

#signature(request, secret) ⇒ Object



231
232
233
234
235
236
237
# File 'lib/crash_log/auth_hmac.rb', line 231

def signature(request, secret)
  digest = OpenSSL::Digest::Digest.new('sha1')
  string = canonical_string(request)
  hmac = OpenSSL::HMAC.digest(digest, secret, string)
  encoded_signature = Base64.encode64(hmac)
  encoded_signature.gsub(/\n/, '').strip
end