warden-hmac-authentication 0.3.0 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env ruby
2
2
 
3
3
  require 'trollop'
4
- require 'hmac_signer'
4
+ require 'hmac/signer'
5
5
 
6
6
  opts = Trollop::options do
7
7
 
@@ -38,7 +38,7 @@ url = ARGV.shift
38
38
  secret = opts.delete(:secret)
39
39
  algorithm = opts.delete(:algorithm)
40
40
 
41
- signer = HMACSigner.new(algorithm)
41
+ signer = HMAC::Signer.new(algorithm)
42
42
 
43
43
  if "sign" == cmd
44
44
  puts signer.sign_url(url, secret, opts)
@@ -51,4 +51,4 @@ else
51
51
  puts "URL #{url} does not contain a valid signature"
52
52
  exit 1
53
53
  end
54
- end
54
+ end
@@ -0,0 +1,245 @@
1
+ require 'addressable/uri'
2
+ require 'openssl'
3
+ require 'rack/utils'
4
+ if defined?(JRUBY_VERSION) && RUBY_VERSION =~ /^1\.9/
5
+ require 'hmac/string/jruby'
6
+ end
7
+
8
+ module HMAC
9
+ # Helper class that provides signing capabilites for the hmac strategies.
10
+ #
11
+ # @author Felix Gilcher <felix.gilcher@asquera.de>
12
+ class Signer
13
+ attr_accessor :secret, :algorithm, :default_opts
14
+
15
+ # create a new HMAC instance
16
+ #
17
+ # @param [String] algorithm The hashing-algorithm to use. See the openssl documentation for valid values.
18
+ # @param [Hash] default_opts The default options for all calls that take opts
19
+ #
20
+ # @option default_opts [String] :auth_scheme ('HMAC') The name of the authorization scheme used in the Authorization header and to construct various header-names
21
+ # @option default_opts [String] :auth_param ('auth') The name of the authentication param to use for query based authentication
22
+ # @option default_opts [String] :auth_header ('Authorization') The name of the authorization header to use
23
+ # @option default_opts [String] :auth_header_format ('%{auth_scheme} %{signature}') The format of the authorization header. Will be interpolated with the given options and the signature.
24
+ # @option default_opts [String] :nonce_header ('X-#{auth_scheme}-Nonce') The header name for the request nonce
25
+ # @option default_opts [String] :alternate_date_header ('X-#{auth_scheme}-Date') The header name for the alternate date header
26
+ # @option default_opts [Bool] :query_based (false) Whether to use query based authentication
27
+ # @option default_opts [Bool] :use_alternate_date_header (false) Use the alternate date header instead of `Date`
28
+ #
29
+ def initialize(algorithm = "sha1", default_opts = {})
30
+ self.algorithm = algorithm
31
+ self.default_opts = {
32
+ :auth_scheme => "HMAC",
33
+ :auth_param => "auth",
34
+ :auth_header => "Authorization",
35
+ :auth_header_format => "%{auth_scheme} %{signature}",
36
+ :nonce_header => "X-%{scheme}-Nonce" % {:scheme => (default_opts[:auth_scheme] || "HMAC")},
37
+ :alternate_date_header => "X-%{scheme}-Date" % {:scheme => (default_opts[:auth_scheme] || "HMAC")},
38
+ :query_based => false,
39
+ :use_alternate_date_header => false
40
+ }.merge(default_opts)
41
+
42
+ end
43
+
44
+ # Generate the signature from a hash representation
45
+ #
46
+ # returns nil if no secret or an empty secret was given
47
+ #
48
+ # @param [Hash] params the parameters to create the representation with
49
+ # @option params [String] :secret The secret to generate the signature with
50
+ # @option params [String] :method The HTTP Verb of the request
51
+ # @option params [String] :date The date of the request as it was formatted in the request
52
+ # @option params [String] :nonce ('') The nonce given in the request
53
+ # @option params [String] :path The path portion of the request
54
+ # @option params [Hash] :query ({}) The query parameters given in the request. Must not contain the auth param.
55
+ # @option params [Hash] :headers ({}) All headers given in the request (optional and required)
56
+ # @option params [String] :auth_scheme ('HMAC') The name of the authorization scheme used in the Authorization header and to construct various header-names
57
+ # @option params [String] :auth_param ('auth') The name of the authentication param to use for query based authentication
58
+ # @option params [String] :auth_header ('Authorization') The name of the authorization header to use
59
+ # @option params [String] :auth_header_format ('%{auth_scheme} %{signature}') The format of the authorization header. Will be interpolated with the given options and the signature.
60
+ # @option params [String] :nonce_header ('X-#{auth_scheme}-Nonce') The header name for the request nonce
61
+ # @option params [String] :alternate_date_header ('X-#{auth_scheme}-Date') The header name for the alternate date header
62
+ # @option params [Bool] :query_based (false) Whether to use query based authentication
63
+ # @option params [Bool] :use_alternate_date_header (false) Use the alternate date header instead of `Date`
64
+ #
65
+ # @return [String] the signature
66
+ def generate_signature(params)
67
+ secret = params.delete(:secret)
68
+
69
+ # jruby stumbles over empty secrets, we regard them as invalid anyways, so we return an empty digest if no scret was given
70
+ if '' == secret.to_s
71
+ nil
72
+ else
73
+ OpenSSL::HMAC.hexdigest(algorithm, secret, canonical_representation(params))
74
+ end
75
+ end
76
+
77
+ # compares the given signature with the signature created from a hash representation
78
+ #
79
+ # @param [String] signature the signature to compare with
80
+ # @param [Hash] params the parameters to create the representation with
81
+ # @option params [String] :secret The secret to generate the signature with
82
+ # @option params [String] :method The HTTP Verb of the request
83
+ # @option params [String] :date The date of the request as it was formatted in the request
84
+ # @option params [String] :nonce ('') The nonce given in the request
85
+ # @option params [String] :path The path portion of the request
86
+ # @option params [Hash] :query ({}) The query parameters given in the request. Must not contain the auth param.
87
+ # @option params [Hash] :headers ({}) All headers given in the request (optional and required)
88
+ # @option params [String] :auth_scheme ('HMAC') The name of the authorization scheme used in the Authorization header and to construct various header-names
89
+ # @option params [String] :auth_param ('auth') The name of the authentication param to use for query based authentication
90
+ # @option params [String] :auth_header ('Authorization') The name of the authorization header to use
91
+ # @option params [String] :auth_header_format ('%{auth_scheme} %{signature}') The format of the authorization header. Will be interpolated with the given options and the signature.
92
+ # @option params [String] :nonce_header ('X-#{auth_scheme}-Nonce') The header name for the request nonce
93
+ # @option params [String] :alternate_date_header ('X-#{auth_scheme}-Date') The header name for the alternate date header
94
+ # @option params [Bool] :query_based (false) Whether to use query based authentication
95
+ # @option params [Bool] :use_alternate_date_header (false) Use the alternate date header instead of `Date`
96
+ #
97
+ # @return [Bool] true if the signature matches
98
+ def validate_signature(signature, params)
99
+ signature == generate_signature(params)
100
+ end
101
+
102
+ # convienience method to check the signature of a url with query-based authentication
103
+ #
104
+ # @param [String] url the url to test
105
+ # @param [String] secret the secret used to sign the url
106
+ # @param [Hash] opts Options controlling the singature generation
107
+ #
108
+ # @option opts [String] :auth_param ('auth') The name of the authentication param to use for query based authentication
109
+ #
110
+ # @return [Bool] true if the signature is valid
111
+ def validate_url_signature(url, secret, opts = {})
112
+ opts = default_opts.merge(opts)
113
+ opts[:query_based] = true
114
+
115
+ uri = Addressable::URI.parse(url)
116
+ query_values = uri.query_values
117
+ auth_params = query_values.delete(opts[:auth_param])
118
+
119
+ date = auth_params["date"]
120
+ nonce = auth_params["nonce"]
121
+ validate_signature(auth_params["signature"], :secret => secret, :method => "GET", :path => uri.path, :date => date, :nonce => nonce, :query => query_values, :headers => {})
122
+ end
123
+
124
+ # generates the canonical representation for a given request
125
+ #
126
+ # @param [Hash] params the parameters to create the representation with
127
+ # @option params [String] :method The HTTP Verb of the request
128
+ # @option params [String] :date The date of the request as it was formatted in the request
129
+ # @option params [String] :nonce ('') The nonce given in the request
130
+ # @option params [String] :path The path portion of the request
131
+ # @option params [Hash] :query ({}) The query parameters given in the request. Must not contain the auth param.
132
+ # @option params [Hash] :headers ({}) All headers given in the request (optional and required)
133
+ # @option params [String] :auth_scheme ('HMAC') The name of the authorization scheme used in the Authorization header and to construct various header-names
134
+ # @option params [String] :auth_param ('auth') The name of the authentication param to use for query based authentication
135
+ # @option params [String] :auth_header ('Authorization') The name of the authorization header to use
136
+ # @option params [String] :auth_header_format ('%{auth_scheme} %{signature}') The format of the authorization header. Will be interpolated with the given options and the signature.
137
+ # @option params [String] :nonce_header ('X-#{auth_scheme}-Nonce') The header name for the request nonce
138
+ # @option params [String] :alternate_date_header ('X-#{auth_scheme}-Date') The header name for the alternate date header
139
+ # @option params [Bool] :query_based (false) Whether to use query based authentication
140
+ # @option params [Bool] :use_alternate_date_header (false) Use the alternate date header instead of `Date`
141
+ #
142
+ # @return [String] the canonical representation
143
+ def canonical_representation(params)
144
+ rep = ""
145
+
146
+ rep << "#{params[:method].upcase}\n"
147
+ rep << "date:#{params[:date]}\n"
148
+ rep << "nonce:#{params[:nonce]}\n"
149
+
150
+ (params[:headers] || {}).sort.each do |pair|
151
+ name,value = *pair
152
+ rep << "#{name.downcase}:#{value}\n"
153
+ end
154
+
155
+ rep << params[:path]
156
+
157
+ p = (params[:query] || {}).dup
158
+
159
+ if !p.empty?
160
+ query = p.sort.map do |key, value|
161
+ "%{key}=%{value}" % {
162
+ :key => Rack::Utils.unescape(key.to_s),
163
+ :value => Rack::Utils.unescape(value.to_s)
164
+ }
165
+ end.join("&")
166
+ rep << "?#{query}"
167
+ end
168
+
169
+ rep
170
+ end
171
+
172
+ # sign the given request
173
+ #
174
+ # @param [String] url The url of the request
175
+ # @param [String] secret The shared secret for the signature
176
+ # @param [Hash] opts Options for the signature generation
177
+ #
178
+ # @option opts [String] :nonce ('') The nonce to use in the signature
179
+ # @option opts [String, #strftime] :date (Time.now) The date to use in the signature
180
+ # @option opts [Hash] :headers ({}) A list of optional headers to include in the signature
181
+ #
182
+ # @option opts [String] :auth_scheme ('HMAC') The name of the authorization scheme used in the Authorization header and to construct various header-names
183
+ # @option opts [String] :auth_param ('auth') The name of the authentication param to use for query based authentication
184
+ # @option opts [String] :auth_header ('Authorization') The name of the authorization header to use
185
+ # @option opts [String] :auth_header_format ('%{auth_scheme} %{signature}') The format of the authorization header. Will be interpolated with the given options and the signature.
186
+ # @option opts [String] :nonce_header ('X-#{auth_scheme}-Nonce') The header name for the request nonce
187
+ # @option opts [String] :alternate_date_header ('X-#{auth_scheme}-Date') The header name for the alternate date header
188
+ # @option opts [Bool] :query_based (false) Whether to use query based authentication
189
+ # @option opts [Bool] :use_alternate_date_header (false) Use the alternate date header instead of `Date`
190
+ #
191
+ def sign_request(url, secret, opts = {})
192
+ opts = default_opts.merge(opts)
193
+
194
+ uri = Addressable::URI.parse(url)
195
+ headers = opts[:headers] || {}
196
+
197
+ date = opts[:date] || Time.now.gmtime
198
+ date = date.gmtime.strftime('%a, %e %b %Y %T GMT') if date.respond_to? :strftime
199
+
200
+ signature = generate_signature(:secret => secret, :method => "GET", :path => uri.path, :date => date, :nonce => opts[:nonce], :query => uri.query_values, :headers => opts[:headers])
201
+
202
+ if opts[:query_based]
203
+ auth_params = {
204
+ "date" => date,
205
+ "signature" => signature
206
+ }
207
+ auth_params[:nonce] = opts[:nonce] unless opts[:nonce].nil?
208
+
209
+ query_values = uri.query_values || {}
210
+ query_values[opts[:auth_param]] = auth_params
211
+ uri.query_values = query_values
212
+ else
213
+ headers[opts[:auth_header]] = opts[:auth_header_format] % opts.merge({:signature => signature})
214
+ headers[opts[:nonce_header]] = opts[:nonce] unless opts[:nonce].nil?
215
+
216
+ if opts[:use_alternate_date_header]
217
+ headers[opts[:alternate_date_header]] = date
218
+ else
219
+ headers["Date"] = date
220
+ end
221
+ end
222
+
223
+ [headers, uri.to_s]
224
+ end
225
+
226
+ # convienience method to sign a url for use with query-based authentication
227
+ #
228
+ # @param [String] url the url to sign
229
+ # @param [String] secret the secret used to sign the url
230
+ # @param [Hash] opts Options controlling the singature generation
231
+ #
232
+ # @option opts [String] :auth_param ('auth') The name of the authentication param to use for query based authentication
233
+ #
234
+ # @return [String] The signed url
235
+ def sign_url(url, secret, opts = {})
236
+ opts = default_opts.merge(opts)
237
+ opts[:query_based] = true
238
+
239
+ headers, url = *sign_request(url, secret, opts)
240
+ url
241
+ end
242
+
243
+
244
+ end
245
+ end
@@ -0,0 +1,178 @@
1
+ require 'hmac/signer'
2
+ require 'warden'
3
+
4
+
5
+ module Warden
6
+ module Strategies
7
+ module HMAC
8
+ # Base class for hmac authentication in warden. Provides shared methods such as config access
9
+ # and various helpers.
10
+ #
11
+ # @author Felix Gilcher <felix.gilcher@asquera.de>
12
+ class Base < Warden::Strategies::Base
13
+
14
+
15
+ # Performs authentication. Calls success! if authentication was performed successfully and halt!
16
+ # if the authentication information is invalid.
17
+ #
18
+ # Delegates parts of the work to signature_valid? which must be implemented in child-strategies.
19
+ #
20
+ # @see https://github.com/hassox/warden/wiki/Strategies
21
+ def authenticate!
22
+ if "" == secret.to_s
23
+ debug("authentication attempt with an empty secret")
24
+ return fail!("Cannot authenticate with an empty secret")
25
+ end
26
+
27
+ if check_ttl? && !timestamp_valid?
28
+ debug("authentication attempt with an invalid timestamp. Given was #{timestamp}, expected was #{Time.now.gmtime}")
29
+ return fail!("Invalid timestamp")
30
+ end
31
+
32
+ if signature_valid?
33
+ success!(retrieve_user)
34
+ else
35
+ debug("authentication attempt with an invalid signature.")
36
+ fail!("Invalid token passed")
37
+ end
38
+ end
39
+
40
+ # Retrieve the current request method
41
+ #
42
+ # @return [String] The request method in capital letters
43
+ def request_method
44
+ env['REQUEST_METHOD'].upcase
45
+ end
46
+
47
+ # Retrieve the request query parameters
48
+ #
49
+ # @return [Hash] The query parameters
50
+ def params
51
+ request.GET
52
+ end
53
+
54
+ # Retrieve the request headers. Header names are normalized by this method by stripping
55
+ # the `HTTP_`-prefix and replacing underscores with dashes. `HTTP_X_Foo` is normalized to
56
+ # `X-Foo`.
57
+ #
58
+ # @return [Hash] The request headers
59
+ def headers
60
+ pairs = env.select {|k,v| k.start_with? 'HTTP_'}
61
+ .collect {|pair| [pair[0].sub(/^HTTP_/, '').gsub(/_/, '-'), pair[1]]}
62
+ .sort
63
+ headers = Hash[*pairs.flatten]
64
+ headers
65
+ end
66
+
67
+ # Retrieve a user from the database. Stub implementation that just returns true, needed for compat.
68
+ #
69
+ # @return [Bool] true
70
+ def retrieve_user
71
+ true
72
+ end
73
+
74
+ # Log a debug message if a logger is available.
75
+ #
76
+ # @param [String] msg The message to log
77
+ def debug(msg)
78
+ if logger
79
+ logger.debug(msg)
80
+ end
81
+ end
82
+
83
+ # Retrieve a logger. Current implementation can
84
+ # only handle Padrino loggers
85
+ #
86
+ # @return [Logger] the logger, nil if none is available
87
+ def logger
88
+ if defined? Padrino
89
+ Padrino.logger
90
+ end
91
+ end
92
+
93
+ private
94
+ def config
95
+ env["warden"].config[:scope_defaults][scope][:hmac]
96
+ end
97
+
98
+ def auth_param
99
+ config[:auth_param] || "auth"
100
+ end
101
+
102
+ def auth_header
103
+ config[:auth_header] || "Authorization"
104
+ end
105
+
106
+ def auth_scheme_name
107
+ config[:auth_scheme] || "HMAC"
108
+ end
109
+
110
+ def nonce_header_name
111
+ config[:nonce_header] || "X-#{auth_scheme_name}-Nonce"
112
+ end
113
+
114
+ def alternate_date_header_name
115
+ config[:alternate_date_header] || "X-#{auth_scheme_name}-Date"
116
+ end
117
+
118
+ def optional_headers
119
+ (config[:optional_headers] || []) + ["Content-MD5", "Content-Type"]
120
+ end
121
+
122
+ def lowercase_headers
123
+
124
+ if @lowercase_headers.nil?
125
+ tmp = headers.map do |name,value|
126
+ [name.downcase, value]
127
+ end
128
+ @lowercase_headers = Hash[*tmp.flatten]
129
+ end
130
+
131
+ @lowercase_headers
132
+ end
133
+
134
+ def hmac
135
+ ::HMAC::Signer.new(algorithm)
136
+ end
137
+
138
+ def algorithm
139
+ config[:algorithm] || "sha1"
140
+ end
141
+
142
+ def ttl
143
+ config[:ttl].to_i
144
+ end
145
+
146
+ def check_ttl?
147
+ !config[:ttl].nil?
148
+ end
149
+
150
+ def timestamp
151
+ Time.strptime(request_timestamp, '%a, %e %b %Y %T %z') unless request_timestamp.nil?
152
+ end
153
+
154
+ def has_timestamp?
155
+ !timestamp.nil?
156
+ end
157
+
158
+ def timestamp_valid?
159
+ now = Time.now.gmtime.to_i
160
+ timestamp.to_i <= (now + clockskew) && timestamp.to_i >= (now - ttl)
161
+ end
162
+
163
+ def nonce_required?
164
+ !!config[:require_nonce]
165
+ end
166
+
167
+ def secret
168
+ @secret ||= config[:secret].respond_to?(:call) ? config[:secret].call(self) : config[:secret]
169
+ end
170
+
171
+ def clockskew
172
+ (config[:clockskew] || 5)
173
+ end
174
+
175
+ end
176
+ end
177
+ end
178
+ end
@@ -0,0 +1,98 @@
1
+ require_relative 'base'
2
+
3
+ module Warden
4
+ module Strategies
5
+ module HMAC
6
+ # Implements header-based hmac authentication for warden. The strategy is registered as
7
+ # `:hmac_header` in the warden strategy list.
8
+ #
9
+ # @author Felix Gilcher <felix.gilcher@asquera.de>
10
+ class Header < Warden::Strategies::HMAC::Base
11
+
12
+ # Checks that this strategy applies. Tests that the required
13
+ # authentication information was given.
14
+ #
15
+ # @return [Bool] true if all required authentication information is available in the request
16
+ # @see https://github.com/hassox/warden/wiki/Strategies
17
+ def valid?
18
+ valid = required_headers.all? { |h| headers.include?(h) } && headers.include?("Authorization") && has_timestamp?
19
+ valid = valid && scheme_valid?
20
+ valid
21
+ end
22
+
23
+ # Check that the signature given in the request is valid.
24
+ #
25
+ # @return [Bool] true if the request is valid
26
+ def signature_valid?
27
+
28
+ #:method => "GET",
29
+ #:date => "Mon, 20 Jun 2011 12:06:11 GMT",
30
+ #:nonce => "TESTNONCE",
31
+ #:path => "/example",
32
+ #:query => {
33
+ # "foo" => "bar",
34
+ # "baz" => "foobared"
35
+ #},
36
+ #:headers => {
37
+ # "Content-Type" => "application/json;charset=utf8",
38
+ # "Content-MD5" => "d41d8cd98f00b204e9800998ecf8427e"
39
+ #}
40
+
41
+ hmac.validate_signature(given_signature, {
42
+ :secret => secret,
43
+ :method => request_method,
44
+ :date => request_timestamp,
45
+ :nonce => nonce,
46
+ :path => request.path,
47
+ :query => params,
48
+ :headers => headers.select {|name, value| optional_headers.include? name}
49
+ })
50
+ end
51
+
52
+ # retrieve the signature from the request
53
+ #
54
+ # @return [String] The signature from the request
55
+ def given_signature
56
+ headers[auth_header].split(" ")[1]
57
+ end
58
+
59
+ # retrieve the nonce from the request
60
+ #
61
+ # @return [String] The nonce or an empty string if no nonce was given in the request
62
+ def nonce
63
+ headers[nonce_header_name]
64
+ end
65
+
66
+ # retrieve the request timestamp as string
67
+ #
68
+ # @return [String] The request timestamp or an empty string if no timestamp was given in the request
69
+ def request_timestamp
70
+ headers[date_header]
71
+ end
72
+
73
+ private
74
+
75
+ def required_headers
76
+ headers = [auth_header]
77
+ headers += [nonce_header_name] if nonce_required?
78
+ headers
79
+ end
80
+
81
+ def scheme_valid?
82
+ headers[auth_header].to_s.split(" ").first == auth_scheme_name
83
+ end
84
+
85
+ def date_header
86
+ if headers.include? alternate_date_header_name
87
+ alternate_date_header_name
88
+ else
89
+ "Date"
90
+ end
91
+ end
92
+
93
+ end
94
+ end
95
+ end
96
+ end
97
+
98
+ Warden::Strategies.add(:hmac_header, Warden::Strategies::HMAC::Header)
@@ -0,0 +1,57 @@
1
+ require_relative 'base'
2
+
3
+ module Warden
4
+ module Strategies
5
+ module HMAC
6
+ # Implements query-based hmac authentication for warden. The strategy is registered as
7
+ # `:hmac_query` in the warden strategy list.
8
+ #
9
+ # @author Felix Gilcher <felix.gilcher@asquera.de>
10
+ class Warden::Strategies::HMAC::Query < Warden::Strategies::HMAC::Base
11
+
12
+ # Checks that this strategy applies. Tests that the required
13
+ # authentication information was given.
14
+ #
15
+ # @return [Bool] true if all required authentication information is available in the request
16
+ # @see https://github.com/hassox/warden/wiki/Strategies
17
+ def valid?
18
+ valid = auth_info.include? "signature"
19
+ valid = valid && has_timestamp? if check_ttl?
20
+ valid = valid && has_nonce? if nonce_required?
21
+ valid
22
+ end
23
+
24
+ # Check that the signature given in the request is valid.
25
+ #
26
+ # @return [Bool] true if the request is valid
27
+ def signature_valid?
28
+ hmac.validate_url_signature(request.url, secret)
29
+ end
30
+
31
+ # retrieve the authentication information from the request
32
+ #
33
+ # @return [Hash] the authentication info in the request
34
+ def auth_info
35
+ params[auth_param] || {}
36
+ end
37
+
38
+ # retrieve the nonce from the request
39
+ #
40
+ # @return [String] The nonce or an empty string if no nonce was given in the request
41
+ def nonce
42
+ auth_info["nonce"] || ""
43
+ end
44
+
45
+ # retrieve the request timestamp as string
46
+ #
47
+ # @return [String] The request timestamp or an empty string if no timestamp was given in the request
48
+ def request_timestamp
49
+ auth_info["date"] || ""
50
+ end
51
+
52
+ end
53
+ end
54
+ end
55
+ end
56
+
57
+ Warden::Strategies.add(:hmac_query, Warden::Strategies::HMAC::Query)
@@ -0,0 +1,33 @@
1
+ # this is a ruby workaround for jruby issue http://jira.codehaus.org/browse/JRUBY-5338
2
+
3
+ if defined?(JRUBY_VERSION) && RUBY_VERSION =~ /^1\.9/
4
+ class KeyError < IndexError
5
+ end
6
+
7
+ class String
8
+ alias_method :old_format, :%
9
+ def %(replacements)
10
+ split_re = /(?<!%)(%{[^}]+})/
11
+ replace_re = /(?<!%)%{([^}]+)}/
12
+ if ! replacements.is_a? Hash
13
+ if split_re.match self
14
+ raise ArgumentError, "one hash required"
15
+ else
16
+ return self.old_format replacements
17
+ end
18
+ end
19
+ segments = self.split split_re
20
+ segments.each_index do |i; md, key|
21
+ md = replace_re.match(segments[i])
22
+ if ! md.nil?
23
+ key = md.captures[0].to_sym
24
+ raise KeyError, "key[#{key}] not found" unless replacements.has_key?(key)
25
+ segments[i] = replacements[key]
26
+ else
27
+ segments[i] = segments[i].gsub "%%", "%"
28
+ end
29
+ end
30
+ segments.join
31
+ end
32
+ end
33
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: warden-hmac-authentication
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.4.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,12 +10,11 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2011-07-16 00:00:00.000000000 +02:00
14
- default_executable:
13
+ date: 2011-12-13 00:00:00.000000000Z
15
14
  dependencies:
16
15
  - !ruby/object:Gem::Dependency
17
16
  name: addressable
18
- requirement: &10322600 !ruby/object:Gem::Requirement
17
+ requirement: &2152266240 !ruby/object:Gem::Requirement
19
18
  none: false
20
19
  requirements:
21
20
  - - ! '>='
@@ -23,10 +22,10 @@ dependencies:
23
22
  version: '0'
24
23
  type: :runtime
25
24
  prerelease: false
26
- version_requirements: *10322600
25
+ version_requirements: *2152266240
27
26
  - !ruby/object:Gem::Dependency
28
27
  name: rack
29
- requirement: &10322100 !ruby/object:Gem::Requirement
28
+ requirement: &2152265520 !ruby/object:Gem::Requirement
30
29
  none: false
31
30
  requirements:
32
31
  - - ! '>='
@@ -34,10 +33,10 @@ dependencies:
34
33
  version: '0'
35
34
  type: :runtime
36
35
  prerelease: false
37
- version_requirements: *10322100
36
+ version_requirements: *2152265520
38
37
  - !ruby/object:Gem::Dependency
39
38
  name: trollop
40
- requirement: &10321580 !ruby/object:Gem::Requirement
39
+ requirement: &2152265000 !ruby/object:Gem::Requirement
41
40
  none: false
42
41
  requirements:
43
42
  - - ! '>='
@@ -45,10 +44,43 @@ dependencies:
45
44
  version: '0'
46
45
  type: :runtime
47
46
  prerelease: false
48
- version_requirements: *10321580
47
+ version_requirements: *2152265000
49
48
  - !ruby/object:Gem::Dependency
50
- name: yard
51
- requirement: &10321140 !ruby/object:Gem::Requirement
49
+ name: warden
50
+ requirement: &2152264180 !ruby/object:Gem::Requirement
51
+ none: false
52
+ requirements:
53
+ - - ! '>='
54
+ - !ruby/object:Gem::Version
55
+ version: '0'
56
+ type: :runtime
57
+ prerelease: false
58
+ version_requirements: *2152264180
59
+ - !ruby/object:Gem::Dependency
60
+ name: rake
61
+ requirement: &2152263320 !ruby/object:Gem::Requirement
62
+ none: false
63
+ requirements:
64
+ - - ! '>='
65
+ - !ruby/object:Gem::Version
66
+ version: '0'
67
+ type: :development
68
+ prerelease: false
69
+ version_requirements: *2152263320
70
+ - !ruby/object:Gem::Dependency
71
+ name: rack-test
72
+ requirement: &2152262180 !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ type: :development
79
+ prerelease: false
80
+ version_requirements: *2152262180
81
+ - !ruby/object:Gem::Dependency
82
+ name: riot
83
+ requirement: &2152261120 !ruby/object:Gem::Requirement
52
84
  none: false
53
85
  requirements:
54
86
  - - ! '>='
@@ -56,10 +88,10 @@ dependencies:
56
88
  version: '0'
57
89
  type: :development
58
90
  prerelease: false
59
- version_requirements: *10321140
91
+ version_requirements: *2152261120
60
92
  - !ruby/object:Gem::Dependency
61
- name: rdiscount
62
- requirement: &10320640 !ruby/object:Gem::Requirement
93
+ name: timecop
94
+ requirement: &2152228000 !ruby/object:Gem::Requirement
63
95
  none: false
64
96
  requirements:
65
97
  - - ! '>='
@@ -67,10 +99,10 @@ dependencies:
67
99
  version: '0'
68
100
  type: :development
69
101
  prerelease: false
70
- version_requirements: *10320640
102
+ version_requirements: *2152228000
71
103
  - !ruby/object:Gem::Dependency
72
104
  name: simplecov
73
- requirement: &10320200 !ruby/object:Gem::Requirement
105
+ requirement: &2152227480 !ruby/object:Gem::Requirement
74
106
  none: false
75
107
  requirements:
76
108
  - - ! '>='
@@ -78,10 +110,10 @@ dependencies:
78
110
  version: '0'
79
111
  type: :development
80
112
  prerelease: false
81
- version_requirements: *10320200
113
+ version_requirements: *2152227480
82
114
  - !ruby/object:Gem::Dependency
83
115
  name: simplecov-html
84
- requirement: &10319660 !ruby/object:Gem::Requirement
116
+ requirement: &2152226940 !ruby/object:Gem::Requirement
85
117
  none: false
86
118
  requirements:
87
119
  - - ! '>='
@@ -89,7 +121,7 @@ dependencies:
89
121
  version: '0'
90
122
  type: :development
91
123
  prerelease: false
92
- version_requirements: *10319660
124
+ version_requirements: *2152226940
93
125
  description: ! "This gem provides request authentication via [HMAC](http://en.wikipedia.org/wiki/Hmac).
94
126
  The main usage is request based, noninteractive\n authentication for API implementations.
95
127
  Two strategies are supported that differ mainly in how the authentication information
@@ -109,12 +141,12 @@ files:
109
141
  - README.md
110
142
  - Rakefile
111
143
  - LICENSE
112
- - lib/strategies/hmac_query_strategy.rb
113
- - lib/strategies/hmac_header_strategy.rb
114
- - lib/strategies/base.rb
115
- - lib/hmac_signer.rb
144
+ - lib/hmac/signer.rb
145
+ - lib/hmac/strategies/base.rb
146
+ - lib/hmac/strategies/header.rb
147
+ - lib/hmac/strategies/query.rb
148
+ - lib/hmac/string/jruby.rb
116
149
  - bin/warden-hmac-authentication
117
- has_rdoc: true
118
150
  homepage: https://github.com/Asquera/warden-hmac-authentication
119
151
  licenses: []
120
152
  post_install_message:
@@ -135,8 +167,9 @@ required_rubygems_version: !ruby/object:Gem::Requirement
135
167
  version: '0'
136
168
  requirements: []
137
169
  rubyforge_project:
138
- rubygems_version: 1.6.2
170
+ rubygems_version: 1.8.6
139
171
  signing_key:
140
172
  specification_version: 3
141
173
  summary: Provides request based, non-interactive authentication for APIs
142
174
  test_files: []
175
+ has_rdoc:
data/lib/hmac_signer.rb DELETED
@@ -1,234 +0,0 @@
1
- require 'addressable/uri'
2
- require 'openssl'
3
- require 'rack/utils'
4
-
5
-
6
- # Helper class that provides signing capabilites for the hmac strategies.
7
- #
8
- # @author Felix Gilcher <felix.gilcher@asquera.de>
9
- class HMACSigner
10
- attr_accessor :secret, :algorithm, :default_opts
11
-
12
- # create a new HMAC instance
13
- #
14
- # @param [String] algorithm The hashing-algorithm to use. See the openssl documentation for valid values.
15
- # @param [Hash] default_opts The default options for all calls that take opts
16
- #
17
- # @option default_opts [String] :auth_scheme ('HMAC') The name of the authorization scheme used in the Authorization header and to construct various header-names
18
- # @option default_opts [String] :auth_param ('auth') The name of the authentication param to use for query based authentication
19
- # @option default_opts [String] :auth_header ('Authorization') The name of the authorization header to use
20
- # @option default_opts [String] :auth_header_format ('%{auth_scheme} %{signature}') The format of the authorization header. Will be interpolated with the given options and the signature.
21
- # @option default_opts [String] :nonce_header ('X-#{auth_scheme}-Nonce') The header name for the request nonce
22
- # @option default_opts [String] :alternate_date_header ('X-#{auth_scheme}-Date') The header name for the alternate date header
23
- # @option default_opts [Bool] :query_based (false) Whether to use query based authentication
24
- # @option default_opts [Bool] :use_alternate_date_header (false) Use the alternate date header instead of `Date`
25
- #
26
- def initialize(algorithm = "sha1", default_opts = {})
27
- self.algorithm = algorithm
28
- self.default_opts = {
29
- :auth_scheme => "HMAC",
30
- :auth_param => "auth",
31
- :auth_header => "Authorization",
32
- :auth_header_format => "%{auth_scheme} %{signature}",
33
- :nonce_header => "X-%{scheme}-Nonce" % {:scheme => (default_opts[:auth_scheme] || "HMAC")},
34
- :alternate_date_header => "X-%{scheme}-Date" % {:scheme => (default_opts[:auth_scheme] || "HMAC")},
35
- :query_based => false,
36
- :use_alternate_date_header => false
37
- }.merge(default_opts)
38
-
39
- end
40
-
41
- # Generate the signature from a hash representation
42
- #
43
- # @param [Hash] params the parameters to create the representation with
44
- # @option params [String] :secret The secret to generate the signature with
45
- # @option params [String] :method The HTTP Verb of the request
46
- # @option params [String] :date The date of the request as it was formatted in the request
47
- # @option params [String] :nonce ('') The nonce given in the request
48
- # @option params [String] :path The path portion of the request
49
- # @option params [Hash] :query ({}) The query parameters given in the request. Must not contain the auth param.
50
- # @option params [Hash] :headers ({}) All headers given in the request (optional and required)
51
- # @option params [String] :auth_scheme ('HMAC') The name of the authorization scheme used in the Authorization header and to construct various header-names
52
- # @option params [String] :auth_param ('auth') The name of the authentication param to use for query based authentication
53
- # @option params [String] :auth_header ('Authorization') The name of the authorization header to use
54
- # @option params [String] :auth_header_format ('%{auth_scheme} %{signature}') The format of the authorization header. Will be interpolated with the given options and the signature.
55
- # @option params [String] :nonce_header ('X-#{auth_scheme}-Nonce') The header name for the request nonce
56
- # @option params [String] :alternate_date_header ('X-#{auth_scheme}-Date') The header name for the alternate date header
57
- # @option params [Bool] :query_based (false) Whether to use query based authentication
58
- # @option params [Bool] :use_alternate_date_header (false) Use the alternate date header instead of `Date`
59
- #
60
- # @return [String] the signature
61
- def generate_signature(params)
62
- secret = params.delete(:secret)
63
- OpenSSL::HMAC.hexdigest(algorithm, secret, canonical_representation(params))
64
- end
65
-
66
- # compares the given signature with the signature created from a hash representation
67
- #
68
- # @param [String] signature the signature to compare with
69
- # @param [Hash] params the parameters to create the representation with
70
- # @option params [String] :secret The secret to generate the signature with
71
- # @option params [String] :method The HTTP Verb of the request
72
- # @option params [String] :date The date of the request as it was formatted in the request
73
- # @option params [String] :nonce ('') The nonce given in the request
74
- # @option params [String] :path The path portion of the request
75
- # @option params [Hash] :query ({}) The query parameters given in the request. Must not contain the auth param.
76
- # @option params [Hash] :headers ({}) All headers given in the request (optional and required)
77
- # @option params [String] :auth_scheme ('HMAC') The name of the authorization scheme used in the Authorization header and to construct various header-names
78
- # @option params [String] :auth_param ('auth') The name of the authentication param to use for query based authentication
79
- # @option params [String] :auth_header ('Authorization') The name of the authorization header to use
80
- # @option params [String] :auth_header_format ('%{auth_scheme} %{signature}') The format of the authorization header. Will be interpolated with the given options and the signature.
81
- # @option params [String] :nonce_header ('X-#{auth_scheme}-Nonce') The header name for the request nonce
82
- # @option params [String] :alternate_date_header ('X-#{auth_scheme}-Date') The header name for the alternate date header
83
- # @option params [Bool] :query_based (false) Whether to use query based authentication
84
- # @option params [Bool] :use_alternate_date_header (false) Use the alternate date header instead of `Date`
85
- #
86
- # @return [Bool] true if the signature matches
87
- def validate_signature(signature, params)
88
- signature == generate_signature(params)
89
- end
90
-
91
- # convienience method to check the signature of a url with query-based authentication
92
- #
93
- # @param [String] url the url to test
94
- # @param [String] secret the secret used to sign the url
95
- # @param [Hash] opts Options controlling the singature generation
96
- #
97
- # @option opts [String] :auth_param ('auth') The name of the authentication param to use for query based authentication
98
- #
99
- # @return [Bool] true if the signature is valid
100
- def validate_url_signature(url, secret, opts = {})
101
- opts = default_opts.merge(opts)
102
- opts[:query_based] = true
103
-
104
- uri = Addressable::URI.parse(url)
105
- query_values = uri.query_values
106
- auth_params = query_values.delete(opts[:auth_param])
107
-
108
- date = auth_params["date"]
109
- nonce = auth_params["nonce"]
110
- validate_signature(auth_params["signature"], :secret => secret, :method => "GET", :path => uri.path, :date => date, :nonce => nonce, :query => query_values, :headers => {})
111
- end
112
-
113
- # generates the canonical representation for a given request
114
- #
115
- # @param [Hash] params the parameters to create the representation with
116
- # @option params [String] :method The HTTP Verb of the request
117
- # @option params [String] :date The date of the request as it was formatted in the request
118
- # @option params [String] :nonce ('') The nonce given in the request
119
- # @option params [String] :path The path portion of the request
120
- # @option params [Hash] :query ({}) The query parameters given in the request. Must not contain the auth param.
121
- # @option params [Hash] :headers ({}) All headers given in the request (optional and required)
122
- # @option params [String] :auth_scheme ('HMAC') The name of the authorization scheme used in the Authorization header and to construct various header-names
123
- # @option params [String] :auth_param ('auth') The name of the authentication param to use for query based authentication
124
- # @option params [String] :auth_header ('Authorization') The name of the authorization header to use
125
- # @option params [String] :auth_header_format ('%{auth_scheme} %{signature}') The format of the authorization header. Will be interpolated with the given options and the signature.
126
- # @option params [String] :nonce_header ('X-#{auth_scheme}-Nonce') The header name for the request nonce
127
- # @option params [String] :alternate_date_header ('X-#{auth_scheme}-Date') The header name for the alternate date header
128
- # @option params [Bool] :query_based (false) Whether to use query based authentication
129
- # @option params [Bool] :use_alternate_date_header (false) Use the alternate date header instead of `Date`
130
- #
131
- # @return [String] the canonical representation
132
- def canonical_representation(params)
133
- rep = ""
134
-
135
- rep << "#{params[:method].upcase}\n"
136
- rep << "date:#{params[:date]}\n"
137
- rep << "nonce:#{params[:nonce]}\n"
138
-
139
- (params[:headers] || {}).sort.each do |pair|
140
- name,value = *pair
141
- rep << "#{name.downcase}:#{value}\n"
142
- end
143
-
144
- rep << params[:path]
145
-
146
- p = (params[:query] || {}).dup
147
-
148
- if !p.empty?
149
- query = p.sort.map do |key, value|
150
- "%{key}=%{value}" % {
151
- :key => Rack::Utils.unescape(key.to_s),
152
- :value => Rack::Utils.unescape(value.to_s)
153
- }
154
- end.join("&")
155
- rep << "?#{query}"
156
- end
157
-
158
- rep
159
- end
160
-
161
- # sign the given request
162
- #
163
- # @param [String] url The url of the request
164
- # @param [String] secret The shared secret for the signature
165
- # @param [Hash] opts Options for the signature generation
166
- #
167
- # @option opts [String] :nonce ('') The nonce to use in the signature
168
- # @option opts [String, #strftime] :date (Time.now) The date to use in the signature
169
- # @option opts [Hash] :headers ({}) A list of optional headers to include in the signature
170
- #
171
- # @option opts [String] :auth_scheme ('HMAC') The name of the authorization scheme used in the Authorization header and to construct various header-names
172
- # @option opts [String] :auth_param ('auth') The name of the authentication param to use for query based authentication
173
- # @option opts [String] :auth_header ('Authorization') The name of the authorization header to use
174
- # @option opts [String] :auth_header_format ('%{auth_scheme} %{signature}') The format of the authorization header. Will be interpolated with the given options and the signature.
175
- # @option opts [String] :nonce_header ('X-#{auth_scheme}-Nonce') The header name for the request nonce
176
- # @option opts [String] :alternate_date_header ('X-#{auth_scheme}-Date') The header name for the alternate date header
177
- # @option opts [Bool] :query_based (false) Whether to use query based authentication
178
- # @option opts [Bool] :use_alternate_date_header (false) Use the alternate date header instead of `Date`
179
- #
180
- def sign_request(url, secret, opts = {})
181
- opts = default_opts.merge(opts)
182
-
183
- uri = Addressable::URI.parse(url)
184
- headers = opts[:headers] || {}
185
-
186
- date = opts[:date] || Time.now.gmtime
187
- date = date.gmtime.strftime('%a, %e %b %Y %T GMT') if date.respond_to? :strftime
188
-
189
- signature = generate_signature(:secret => secret, :method => "GET", :path => uri.path, :date => date, :nonce => opts[:nonce], :query => uri.query_values, :headers => opts[:headers])
190
-
191
- if opts[:query_based]
192
- auth_params = {
193
- "date" => date,
194
- "signature" => signature
195
- }
196
- auth_params[:nonce] = opts[:nonce] unless opts[:nonce].nil?
197
-
198
- query_values = uri.query_values || {}
199
- query_values[opts[:auth_param]] = auth_params
200
- uri.query_values = query_values
201
- else
202
- headers[opts[:auth_header]] = opts[:auth_header_format] % opts.merge({:signature => signature})
203
- headers[opts[:nonce_header]] = opts[:nonce] unless opts[:nonce].nil?
204
-
205
- if opts[:use_alternate_date_header]
206
- headers[opts[:alternate_date_header]] = date
207
- else
208
- headers["Date"] = date
209
- end
210
- end
211
-
212
- [headers, uri.to_s]
213
- end
214
-
215
- # convienience method to sign a url for use with query-based authentication
216
- #
217
- # @param [String] url the url to sign
218
- # @param [String] secret the secret used to sign the url
219
- # @param [Hash] opts Options controlling the singature generation
220
- #
221
- # @option opts [String] :auth_param ('auth') The name of the authentication param to use for query based authentication
222
- #
223
- # @return [String] The signed url
224
- def sign_url(url, secret, opts = {})
225
- opts = default_opts.merge(opts)
226
- opts[:query_based] = true
227
-
228
- headers, url = *sign_request(url, secret, opts)
229
- url
230
- end
231
-
232
-
233
- end
234
-
@@ -1,173 +0,0 @@
1
- require 'hmac_signer'
2
- require 'warden'
3
-
4
-
5
- # Base class for hmac authentication in warden. Provides shared methods such as config access
6
- # and various helpers.
7
- #
8
- # @author Felix Gilcher <felix.gilcher@asquera.de>
9
- class Warden::Strategies::HMACBase < Warden::Strategies::Base
10
-
11
-
12
- # Performs authentication. Calls success! if authentication was performed successfully and halt!
13
- # if the authentication information is invalid.
14
- #
15
- # Delegates parts of the work to signature_valid? which must be implemented in child-strategies.
16
- #
17
- # @see https://github.com/hassox/warden/wiki/Strategies
18
- def authenticate!
19
- if "" == secret.to_s
20
- debug("authentication attempt with an empty secret")
21
- return fail!("Cannot authenticate with an empty secret")
22
- end
23
-
24
- if check_ttl? && !timestamp_valid?
25
- debug("authentication attempt with an invalid timestamp. Given was #{timestamp}, expected was #{Time.now.gmtime}")
26
- return fail!("Invalid timestamp")
27
- end
28
-
29
- if signature_valid?
30
- success!(retrieve_user)
31
- else
32
- debug("authentication attempt with an invalid signature.")
33
- fail!("Invalid token passed")
34
- end
35
- end
36
-
37
- # Retrieve the current request method
38
- #
39
- # @return [String] The request method in capital letters
40
- def request_method
41
- env['REQUEST_METHOD'].upcase
42
- end
43
-
44
- # Retrieve the request query parameters
45
- #
46
- # @return [Hash] The query parameters
47
- def params
48
- request.GET
49
- end
50
-
51
- # Retrieve the request headers. Header names are normalized by this method by stripping
52
- # the `HTTP_`-prefix and replacing underscores with dashes. `HTTP_X_Foo` is normalized to
53
- # `X-Foo`.
54
- #
55
- # @return [Hash] The request headers
56
- def headers
57
- pairs = env.select {|k,v| k.start_with? 'HTTP_'}
58
- .collect {|pair| [pair[0].sub(/^HTTP_/, '').gsub(/_/, '-'), pair[1]]}
59
- .sort
60
- headers = Hash[*pairs.flatten]
61
- headers
62
- end
63
-
64
- # Retrieve a user from the database. Stub implementation that just returns true, needed for compat.
65
- #
66
- # @return [Bool] true
67
- def retrieve_user
68
- true
69
- end
70
-
71
- # Log a debug message if a logger is available.
72
- #
73
- # @param [String] msg The message to log
74
- def debug(msg)
75
- if logger
76
- logger.debug(msg)
77
- end
78
- end
79
-
80
- # Retrieve a logger. Current implementation can
81
- # only handle Padrino loggers
82
- #
83
- # @return [Logger] the logger, nil if none is available
84
- def logger
85
- if defined? Padrino
86
- Padrino.logger
87
- end
88
- end
89
-
90
- private
91
- def config
92
- env["warden"].config[:scope_defaults][scope][:hmac]
93
- end
94
-
95
- def auth_param
96
- config[:auth_param] || "auth"
97
- end
98
-
99
- def auth_header
100
- config[:auth_header] || "Authorization"
101
- end
102
-
103
- def auth_scheme_name
104
- config[:auth_scheme] || "HMAC"
105
- end
106
-
107
- def nonce_header_name
108
- config[:nonce_header] || "X-#{auth_scheme_name}-Nonce"
109
- end
110
-
111
- def alternate_date_header_name
112
- config[:alternate_date_header] || "X-#{auth_scheme_name}-Date"
113
- end
114
-
115
- def optional_headers
116
- (config[:optional_headers] || []) + ["Content-MD5", "Content-Type"]
117
- end
118
-
119
- def lowercase_headers
120
-
121
- if @lowercase_headers.nil?
122
- tmp = headers.map do |name,value|
123
- [name.downcase, value]
124
- end
125
- @lowercase_headers = Hash[*tmp.flatten]
126
- end
127
-
128
- @lowercase_headers
129
- end
130
-
131
- def hmac
132
- HMACSigner.new(algorithm)
133
- end
134
-
135
- def algorithm
136
- config[:algorithm] || "sha1"
137
- end
138
-
139
- def ttl
140
- config[:ttl].to_i
141
- end
142
-
143
- def check_ttl?
144
- !config[:ttl].nil?
145
- end
146
-
147
- def timestamp
148
- Time.strptime(request_timestamp, '%a, %e %b %Y %T %z') unless request_timestamp.nil?
149
- end
150
-
151
- def has_timestamp?
152
- !timestamp.nil?
153
- end
154
-
155
- def timestamp_valid?
156
- now = Time.now.gmtime.to_i
157
- timestamp.to_i <= (now + clockskew) && timestamp.to_i >= (now - ttl)
158
- end
159
-
160
- def nonce_required?
161
- !!config[:require_nonce]
162
- end
163
-
164
- def secret
165
- @secret ||= config[:secret].respond_to?(:call) ? config[:secret].call(self) : config[:secret]
166
- end
167
-
168
- def clockskew
169
- (config[:clockskew] || 5)
170
- end
171
-
172
-
173
- end
@@ -1,94 +0,0 @@
1
- require_relative 'base'
2
-
3
- # Implements header-based hmac authentication for warden. The strategy is registered as
4
- # `:hmac_header` in the warden strategy list.
5
- #
6
- # @author Felix Gilcher <felix.gilcher@asquera.de>
7
- class Warden::Strategies::HMACHeader < Warden::Strategies::HMACBase
8
-
9
- # Checks that this strategy applies. Tests that the required
10
- # authentication information was given.
11
- #
12
- # @return [Bool] true if all required authentication information is available in the request
13
- # @see https://github.com/hassox/warden/wiki/Strategies
14
- def valid?
15
- valid = required_headers.all? { |h| headers.include?(h) } && headers.include?("Authorization") && has_timestamp?
16
- valid = valid && scheme_valid?
17
- valid
18
- end
19
-
20
- # Check that the signature given in the request is valid.
21
- #
22
- # @return [Bool] true if the request is valid
23
- def signature_valid?
24
-
25
- #:method => "GET",
26
- #:date => "Mon, 20 Jun 2011 12:06:11 GMT",
27
- #:nonce => "TESTNONCE",
28
- #:path => "/example",
29
- #:query => {
30
- # "foo" => "bar",
31
- # "baz" => "foobared"
32
- #},
33
- #:headers => {
34
- # "Content-Type" => "application/json;charset=utf8",
35
- # "Content-MD5" => "d41d8cd98f00b204e9800998ecf8427e"
36
- #}
37
-
38
- hmac.validate_signature(given_signature, {
39
- :secret => secret,
40
- :method => request_method,
41
- :date => request_timestamp,
42
- :nonce => nonce,
43
- :path => request.path,
44
- :query => params,
45
- :headers => headers.select {|name, value| optional_headers.include? name}
46
- })
47
- end
48
-
49
- # retrieve the signature from the request
50
- #
51
- # @return [String] The signature from the request
52
- def given_signature
53
- headers[auth_header].split(" ")[1]
54
- end
55
-
56
- # retrieve the nonce from the request
57
- #
58
- # @return [String] The nonce or an empty string if no nonce was given in the request
59
- def nonce
60
- headers[nonce_header_name]
61
- end
62
-
63
- # retrieve the request timestamp as string
64
- #
65
- # @return [String] The request timestamp or an empty string if no timestamp was given in the request
66
- def request_timestamp
67
- headers[date_header]
68
- end
69
-
70
- private
71
-
72
- def required_headers
73
- headers = [auth_header]
74
- headers += [nonce_header_name] if nonce_required?
75
- headers
76
- end
77
-
78
- def scheme_valid?
79
- headers[auth_header].to_s.split(" ").first == auth_scheme_name
80
- end
81
-
82
- def date_header
83
- if headers.include? alternate_date_header_name
84
- alternate_date_header_name
85
- else
86
- "Date"
87
- end
88
- end
89
-
90
-
91
-
92
- end
93
-
94
- Warden::Strategies.add(:hmac_header, Warden::Strategies::HMACHeader)
@@ -1,52 +0,0 @@
1
- require_relative 'base'
2
-
3
-
4
- # Implements query-based hmac authentication for warden. The strategy is registered as
5
- # `:hmac_query` in the warden strategy list.
6
- #
7
- # @author Felix Gilcher <felix.gilcher@asquera.de>
8
- class Warden::Strategies::HMACQuery < Warden::Strategies::HMACBase
9
-
10
- # Checks that this strategy applies. Tests that the required
11
- # authentication information was given.
12
- #
13
- # @return [Bool] true if all required authentication information is available in the request
14
- # @see https://github.com/hassox/warden/wiki/Strategies
15
- def valid?
16
- valid = auth_info.include? "signature"
17
- valid = valid && has_timestamp? if check_ttl?
18
- valid = valid && has_nonce? if nonce_required?
19
- valid
20
- end
21
-
22
- # Check that the signature given in the request is valid.
23
- #
24
- # @return [Bool] true if the request is valid
25
- def signature_valid?
26
- hmac.validate_url_signature(request.url, secret)
27
- end
28
-
29
- # retrieve the authentication information from the request
30
- #
31
- # @return [Hash] the authentication info in the request
32
- def auth_info
33
- params[auth_param] || {}
34
- end
35
-
36
- # retrieve the nonce from the request
37
- #
38
- # @return [String] The nonce or an empty string if no nonce was given in the request
39
- def nonce
40
- auth_info["nonce"] || ""
41
- end
42
-
43
- # retrieve the request timestamp as string
44
- #
45
- # @return [String] The request timestamp or an empty string if no timestamp was given in the request
46
- def request_timestamp
47
- auth_info["date"] || ""
48
- end
49
-
50
- end
51
-
52
- Warden::Strategies.add(:hmac_query, Warden::Strategies::HMACQuery)