Class: Captivus::AuthHMAC

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

Defined Under Namespace

Modules: Headers Classes: CanonicalString

Constant Summary collapse

VERSION =
"0.0.2"
@@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)


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

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)


194
195
196
197
# File 'lib/captivus/auth_hmac.rb', line 194

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.



166
167
168
# File 'lib/captivus/auth_hmac.rb', line 166

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.



184
185
186
187
# File 'lib/captivus/auth_hmac.rb', line 184

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.



175
176
177
# File 'lib/captivus/auth_hmac.rb', line 175

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)


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

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



246
247
248
# File 'lib/captivus/auth_hmac.rb', line 246

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

#authorization_header(request) ⇒ Object



242
243
244
# File 'lib/captivus/auth_hmac.rb', line 242

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

#canonical_string(request) ⇒ Object



238
239
240
# File 'lib/captivus/auth_hmac.rb', line 238

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)


208
209
210
211
212
# File 'lib/captivus/auth_hmac.rb', line 208

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



232
233
234
235
236
# File 'lib/captivus/auth_hmac.rb', line 232

def signature(request, secret)
  digest = OpenSSL::Digest::Digest.new('sha1')
  string = canonical_string(request)
  Base64.strict_encode64(OpenSSL::HMAC.digest(digest, secret, string)).strip
end