Class: Ey::Hmac::Adapter Abstract

Inherits:
Object
  • Object
show all
Defined in:
lib/ey-hmac/adapter.rb

Overview

This class is abstract.

override methods #method, #path, #body, #content_type and #content_digest

This class is responsible for forming the canonical string to used to sign requests

Direct Known Subclasses

Faraday, Rack

Defined Under Namespace

Classes: Faraday, Rack

Constant Summary collapse

AUTHORIZATION_REGEXP =
/\w+ ([^:]+):(.+)$/

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(request, options = {}) ⇒ Adapter

Returns a new instance of Adapter

Parameters:

  • request (Object)

    signer-specific request implementation

  • options (Hash) (defaults to: {})

    a customizable set of options

Options Hash (options):

  • :version (Integer)

    signature version

  • :ttl (Integer) — default: nil

    duration during which HMAC is valid after signed date

  • :authorization_header (String) — default: 'Authorization'

    Authorization header key.

  • :server (String) — default: 'EyHmac'

    service name prefixed to #authorization. set to #service

  • :sign_with (Symbol) — default: :sha_256

    outgoing signature digest algorithm. See OpenSSL::Digest#new

  • :accepted_digests (Array) — default: [:sha_256]

    accepted incoming signature digest algorithm. See OpenSSL::Digest#new



18
19
20
21
22
23
24
25
26
# File 'lib/ey-hmac/adapter.rb', line 18

def initialize(request, options={})
  @request, @options = request, options

  @ttl                  = options[:ttl]
  @authorization_header = options[:authorization_header] || 'Authorization'
  @service              = options[:service] || 'EyHmac'
  @sign_with            = options[:sign_with] || :sha256
  @accept_digests       = Array(options[:accept_digests] || :sha256)
end

Instance Attribute Details

#accept_digestsObject (readonly)

Returns the value of attribute accept_digests



9
10
11
# File 'lib/ey-hmac/adapter.rb', line 9

def accept_digests
  @accept_digests
end

#authorization_headerObject (readonly)

Returns the value of attribute authorization_header



9
10
11
# File 'lib/ey-hmac/adapter.rb', line 9

def authorization_header
  @authorization_header
end

#optionsObject (readonly)

Returns the value of attribute options



9
10
11
# File 'lib/ey-hmac/adapter.rb', line 9

def options
  @options
end

#requestObject (readonly)

Returns the value of attribute request



9
10
11
# File 'lib/ey-hmac/adapter.rb', line 9

def request
  @request
end

#serviceObject (readonly)

Returns the value of attribute service



9
10
11
# File 'lib/ey-hmac/adapter.rb', line 9

def service
  @service
end

#sign_withObject (readonly)

Returns the value of attribute sign_with



9
10
11
# File 'lib/ey-hmac/adapter.rb', line 9

def sign_with
  @sign_with
end

Instance Method Details

#authenticated!(&block) ⇒ Object Also known as: authenticate!

See Also:

  • Ey::Hmac#authenticate!


116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
# File 'lib/ey-hmac/adapter.rb', line 116

def authenticated!(&block)
  key_id, signature_value = check_signature!
  key_secret = block.call(key_id)

  unless key_secret
    raise Ey::Hmac::MissingSecret,
      "Failed to find secret matching #{key_id.inspect}"
  end

  check_ttl!

  calculated_signatures = accept_digests.map { |ad| signature(key_secret, ad) }
  matching_signature = calculated_signatures.any? { |cs| secure_compare(signature_value, cs) }

  unless matching_signature
    raise Ey::Hmac::SignatureMismatch,
      "Calculated signature #{signature_value} does not match #{calculated_signatures.inspect} using #{canonicalize.inspect}"
  end

  true
end

#authenticated?(options = {}) {|key_id| ... } ⇒ Boolean

Check #authorization_signature against calculated #signature

Yield Parameters:

  • key_id (String)

    public HMAC key

Returns:

  • (Boolean)

    true if block yields matching private key and signature matches, else false

See Also:



109
110
111
112
113
# File 'lib/ey-hmac/adapter.rb', line 109

def authenticated?(options={}, &block)
  authenticated!(&block)
rescue Ey::Hmac::Error
  false
end

#authorization(key_id, key_secret) ⇒ String

Returns HMAC header value of #request

Parameters:

  • key_id (String)

    public HMAC key

  • key_secret (String)

    private HMAC key

Returns:

  • (String)

    HMAC header value of #request



48
49
50
# File 'lib/ey-hmac/adapter.rb', line 48

def authorization(key_id, key_secret)
  "#{service} #{key_id}:#{signature(key_secret, sign_with)}"
end

#authorization_signatureString

This method is abstract.

used when verifying a signed request

Returns value of the #authorization_header

Returns:

Raises:

  • (NotImplementedError)


93
94
95
# File 'lib/ey-hmac/adapter.rb', line 93

def authorization_signature
  raise NotImplementedError
end

#bodyString, NilClass

This method is abstract.

Returns:

  • (String)

    request body.

  • (NilClass)

    if there is no body or the body is empty

Raises:

  • (NotImplementedError)


74
75
76
# File 'lib/ey-hmac/adapter.rb', line 74

def body
  raise NotImplementedError
end

#canonicalizeString

In order for the server to correctly authorize the request, the client and server MUST AGREE on this format

default canonical string formation is '#method\n#content_type\n#content_digest\n#date\n#path'

Returns:

  • (String)

    canonical string used to form the #signature



32
33
34
# File 'lib/ey-hmac/adapter.rb', line 32

def canonicalize
  [method, content_type, content_digest, date, path].join("\n")
end

#content_digestString

This method is abstract.

Digest of body. Default is MD5.

Returns:

  • (String)

    digest of body

Raises:

  • (NotImplementedError)


67
68
69
# File 'lib/ey-hmac/adapter.rb', line 67

def content_digest
  raise NotImplementedError
end

#content_typeString

This method is abstract.

Returns value of the Content-Type header in #request

Returns:

  • (String)

    value of the Content-Type header in #request

Raises:

  • (NotImplementedError)


80
81
82
# File 'lib/ey-hmac/adapter.rb', line 80

def content_type
  raise NotImplementedError
end

#dateString

This method is abstract.

Returns value of the Date header in #request.

Returns:

  • (String)

    value of the Date header in #request.

Raises:

  • (NotImplementedError)

See Also:

  • Time#http_date


87
88
89
# File 'lib/ey-hmac/adapter.rb', line 87

def date
  raise NotImplementedError
end

#methodString

This method is abstract.

Returns upcased request verb. i.e. 'GET'

Returns:

  • (String)

    upcased request verb. i.e. 'GET'

Raises:

  • (NotImplementedError)


54
55
56
# File 'lib/ey-hmac/adapter.rb', line 54

def method
  raise NotImplementedError
end

#pathString

This method is abstract.

Returns request path. i.e. '/blogs/1'

Returns:

  • (String)

    request path. i.e. '/blogs/1'

Raises:

  • (NotImplementedError)


60
61
62
# File 'lib/ey-hmac/adapter.rb', line 60

def path
  raise NotImplementedError
end

#sign!(key_id, key_secret) ⇒ String

This method is abstract.

Add #signature header to request. Typically this is 'Authorization' or 'WWW-Authorization'

Returns:

Raises:

  • (NotImplementedError)

See Also:

  • Ey::Hmac#sign!


101
102
103
# File 'lib/ey-hmac/adapter.rb', line 101

def sign!(key_id, key_secret)
  raise NotImplementedError
end

#signature(key_secret, digest = self.sign_with) ⇒ String

Returns HMAC signature of #request

Parameters:

  • key_secret (String)

    private HMAC key

  • signature (String)

    digest hash function. Defaults to #sign_with

Returns:

  • (String)

    HMAC signature of #request



39
40
41
42
43
# File 'lib/ey-hmac/adapter.rb', line 39

def signature(key_secret, digest = self.sign_with)
  Base64.strict_encode64(
    OpenSSL::HMAC.digest(
      OpenSSL::Digest.new(digest.to_s), key_secret, canonicalize)).strip
end