warden-hmac-authentication 0.3.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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)