signet 0.3.4 → 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.
data/CHANGELOG.md CHANGED
@@ -1,3 +1,8 @@
1
+ # 0.4.0
2
+
3
+ * Added OAuth 1 server implementation
4
+ * Updated Faraday dependency
5
+
1
6
  # 0.3.4
2
7
 
3
8
  * Attempts to auto-detect CA cert location
data/Gemfile CHANGED
@@ -1,20 +1,20 @@
1
1
  source :rubygems
2
2
 
3
3
  gem 'addressable', '>= 2.2.3'
4
- gem 'faraday', '~> 0.7.0'
5
- gem 'multi_json', '>= 1.3.0'
6
- gem 'jwt', '>= 0.1.4'
4
+ gem 'faraday', '~> 0.8.1'
5
+ gem 'multi_json', '>= 1.0.0'
6
+ gem 'jwt', '>= 0.1.5'
7
7
  gem 'extlib', '>= 0.9.15'
8
8
  gem 'jruby-openssl', :platforms => :jruby
9
9
 
10
10
  group :development do
11
- gem 'launchy'
11
+ gem 'launchy', '>= 2.0.0'
12
12
  gem 'yard'
13
13
  gem 'redcarpet'
14
14
  end
15
15
 
16
16
  group :test, :development do
17
17
  gem 'rake', '>= 0.9.0'
18
- gem 'rspec', '~> 1.2.9'
18
+ gem 'rspec', '>= 2.11.0'
19
19
  gem 'rcov', '>= 0.9.9', :platform => :mri_18
20
20
  end
data/Gemfile.lock CHANGED
@@ -3,11 +3,10 @@ GEM
3
3
  specs:
4
4
  addressable (2.2.8)
5
5
  bouncy-castle-java (1.5.0146.1)
6
+ diff-lcs (1.1.3)
6
7
  extlib (0.9.15)
7
- faraday (0.7.6)
8
- addressable (~> 2.2)
8
+ faraday (0.8.1)
9
9
  multipart-post (~> 1.1)
10
- rack (~> 1.1)
11
10
  ffi (1.0.11-java)
12
11
  jruby-openssl (0.7.7)
13
12
  bouncy-castle-java (>= 1.5.0146.1)
@@ -23,11 +22,17 @@ GEM
23
22
  spoon (~> 0.0.1)
24
23
  multi_json (1.3.5)
25
24
  multipart-post (1.1.5)
26
- rack (1.4.1)
27
25
  rake (0.9.2.2)
28
26
  rcov (1.0.0)
29
27
  redcarpet (2.1.1)
30
- rspec (1.2.9)
28
+ rspec (2.10.0)
29
+ rspec-core (~> 2.10.0)
30
+ rspec-expectations (~> 2.10.0)
31
+ rspec-mocks (~> 2.10.0)
32
+ rspec-core (2.10.0)
33
+ rspec-expectations (2.10.0)
34
+ diff-lcs (~> 1.1.3)
35
+ rspec-mocks (2.10.0)
31
36
  spoon (0.0.1)
32
37
  yard (0.8.1)
33
38
 
@@ -38,7 +43,7 @@ PLATFORMS
38
43
  DEPENDENCIES
39
44
  addressable (>= 2.2.3)
40
45
  extlib (>= 0.9.15)
41
- faraday (~> 0.7.0)
46
+ faraday (~> 0.8.1)
42
47
  jruby-openssl
43
48
  jwt (>= 0.1.4)
44
49
  launchy
@@ -46,5 +51,5 @@ DEPENDENCIES
46
51
  rake (>= 0.9.0)
47
52
  rcov (>= 0.9.9)
48
53
  redcarpet
49
- rspec (~> 1.2.9)
54
+ rspec (~> 2.10.0)
50
55
  yard
data/README.md CHANGED
@@ -19,6 +19,7 @@ Signet is an OAuth 1.0 / OAuth 2.0 implementation.
19
19
  - {Signet::OAuth1}
20
20
  - {Signet::OAuth1::Client}
21
21
  - {Signet::OAuth1::Credential}
22
+ - {Signet::OAuth1::Server}
22
23
  - {Signet::OAuth2}
23
24
  - {Signet::OAuth2::Client}
24
25
 
data/Rakefile CHANGED
@@ -5,14 +5,6 @@ $:.uniq!
5
5
  require 'rubygems'
6
6
  require 'rake'
7
7
 
8
- begin
9
- require 'spec/rake/spectask'
10
- rescue LoadError
11
- STDERR.puts "Please install rspec:"
12
- STDERR.puts "sudo gem install rspec"
13
- exit(1)
14
- end
15
-
16
8
  require File.join(File.dirname(__FILE__), 'lib/signet', 'version')
17
9
 
18
10
  PKG_DISPLAY_NAME = 'Signet'
@@ -36,11 +28,11 @@ PKG_FILES = FileList[
36
28
  "[A-Z]*", "Rakefile"
37
29
  ].exclude(/database\.yml/).exclude(/[_\.]git$/)
38
30
 
39
- RCOV_ENABLED = !!(RUBY_PLATFORM != "java" && RUBY_VERSION =~ /^1\.8/)
31
+ RCOV_ENABLED = !!(RUBY_PLATFORM != 'java' && RUBY_VERSION =~ /^1\.8/)
40
32
  if RCOV_ENABLED
41
- task :default => "spec:verify"
33
+ task :default => 'spec:rcov'
42
34
  else
43
- task :default => "spec"
35
+ task :default => 'spec:normal'
44
36
  end
45
37
 
46
38
  WINDOWS = (RUBY_PLATFORM =~ /mswin|win32|mingw|bccwin|cygwin/) rescue false
data/lib/signet/errors.rb CHANGED
@@ -27,7 +27,14 @@ module Signet
27
27
  end
28
28
 
29
29
  ##
30
- # An error indicating the server refused to authorize the client.
30
+ # An error indicating that the server considers the Authorization header to
31
+ # be malformed(missing/unsupported/invalid parameters), and the request
32
+ # should be considered invalid.
33
+ class MalformedAuthorizationError < StandardError
34
+ end
35
+
36
+ ##
37
+ # An error indicating the remote server refused to authorize the client.
31
38
  class AuthorizationError < StandardError
32
39
  ##
33
40
  # Creates a new authentication error.
@@ -12,7 +12,7 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- gem 'faraday', '~> 0.7.0'
15
+ gem 'faraday', '~> 0.8.1'
16
16
  require 'faraday'
17
17
  require 'faraday/utils'
18
18
 
@@ -538,7 +538,8 @@ module Signet
538
538
  options = {
539
539
  :signature_method => 'HMAC-SHA1',
540
540
  :additional_parameters => [],
541
- :realm => nil
541
+ :realm => nil,
542
+ :connection => Faraday.default_connection
542
543
  }.merge(options)
543
544
  method = :post
544
545
  parameters = ::Signet::OAuth1.unsigned_temporary_credential_parameters(
@@ -565,8 +566,10 @@ module Signet
565
566
  headers << ['Content-Type', 'application/x-www-form-urlencoded']
566
567
  headers << ['Content-Length', '0']
567
568
  end
568
- return Faraday::Request.create(method.to_s.downcase.to_sym) do |req|
569
- req.url(Addressable::URI.parse(self.temporary_credential_uri.to_str))
569
+ return options[:connection].build_request(method.to_s.downcase.to_sym) do |req|
570
+ req.url(Addressable::URI.parse(
571
+ self.temporary_credential_uri.to_str
572
+ ).normalize.to_s)
570
573
  req.headers = Faraday::Utils::Headers.new(headers)
571
574
  end
572
575
  end
@@ -603,6 +606,7 @@ module Signet
603
606
  options[:connection] ||= Faraday.default_connection
604
607
  request = self.generate_temporary_credential_request(options)
605
608
  request_env = request.to_env(options[:connection])
609
+ request_env[:request] ||= request
606
610
  response = options[:connection].app.call(request_env)
607
611
  if response.status.to_i == 200
608
612
  return ::Signet::OAuth1.parse_form_encoded_credentials(response.body)
@@ -689,7 +693,8 @@ module Signet
689
693
  end
690
694
  options = {
691
695
  :signature_method => 'HMAC-SHA1',
692
- :realm => nil
696
+ :realm => nil,
697
+ :connection => Faraday.default_connection
693
698
  }.merge(options)
694
699
  method = :post
695
700
  parameters = ::Signet::OAuth1.unsigned_token_credential_parameters(
@@ -718,8 +723,10 @@ module Signet
718
723
  headers << ['Content-Type', 'application/x-www-form-urlencoded']
719
724
  headers << ['Content-Length', '0']
720
725
  end
721
- return Faraday::Request.create(method.to_s.downcase.to_sym) do |req|
722
- req.url(Addressable::URI.parse(self.token_credential_uri.to_str))
726
+ return options[:connection].build_request(method.to_s.downcase.to_sym) do |req|
727
+ req.url(Addressable::URI.parse(
728
+ self.token_credential_uri.to_str
729
+ ).normalize.to_s)
723
730
  req.headers = Faraday::Utils::Headers.new(headers)
724
731
  end
725
732
  end
@@ -754,6 +761,7 @@ module Signet
754
761
  options[:connection] ||= Faraday.default_connection
755
762
  request = self.generate_token_credential_request(options)
756
763
  request_env = request.to_env(options[:connection])
764
+ request_env[:request] ||= request
757
765
  response = options[:connection].app.call(request_env)
758
766
  if response.status.to_i == 200
759
767
  return ::Signet::OAuth1.parse_form_encoded_credentials(response.body)
@@ -889,9 +897,8 @@ module Signet
889
897
  raise TypeError, "Expected String, got #{body.class}."
890
898
  end
891
899
  method = method.to_s.downcase.to_sym
892
-
893
- request = Faraday::Request.create(method) do |req|
894
- req.url(Addressable::URI.parse(uri))
900
+ request = options[:connection].build_request(method) do |req|
901
+ req.url(Addressable::URI.parse(uri).normalize.to_s)
895
902
  req.headers = Faraday::Utils::Headers.new(headers)
896
903
  req.body = body
897
904
  end
@@ -978,6 +985,7 @@ module Signet
978
985
  options[:connection] ||= Faraday.default_connection
979
986
  request = self.generate_authenticated_request(options)
980
987
  request_env = request.to_env(options[:connection])
988
+ request_env[:request] ||= request
981
989
  response = options[:connection].app.call(request_env)
982
990
  if response.status.to_i == 401
983
991
  # When accessing a protected resource, we only want to raise an
@@ -0,0 +1,505 @@
1
+ # Copyright (C) 2011 The Yakima Herald-Republic.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+ #
15
+ require 'faraday'
16
+
17
+ require 'stringio'
18
+ require 'addressable/uri'
19
+ require 'signet'
20
+ require 'signet/errors'
21
+ require 'signet/oauth_1'
22
+ require 'signet/oauth_1/credential'
23
+
24
+ module Signet
25
+ module OAuth1
26
+ class Server
27
+
28
+ # @return [Proc] lookup the value from this Proc.
29
+ attr_accessor :nonce_timestamp, :client_credential, :token_credential,
30
+ :temporary_credential, :verifier
31
+
32
+ ##
33
+ # Creates an OAuth 1.0 server.
34
+ # @overload initialize(options)
35
+ # @param [Proc] nonce_timestamp verify a nonce/timestamp pair.
36
+ # @param [Proc] client_credential find a client credential.
37
+ # @param [Proc] token_credential find a token credential.
38
+ # @param [Proc] temporary_credential find a temporary credential.
39
+ # @param [Proc] verifier validate a verifier value.
40
+ #
41
+ # @example
42
+ # server = Signet::OAuth1::Server.new(
43
+ # :nonce_timestamp =>
44
+ # lambda { |n,t| OauthNonce.remember(n,t) },
45
+ # :client_credential =>
46
+ # lambda { |key| ClientCredential.find_by_key(key).to_hash },
47
+ # :token_credential =>
48
+ # lambda { |key| TokenCredential.find_by_key(key).to_hash },
49
+ # :temporary_credential =>
50
+ # lambda { |key| TemporaryCredential.find_by_key(key).to_hash },
51
+ # :verifier =>
52
+ # lambda {|verifier| Verifier.find_by_verifier(verifier).active? }
53
+ # )
54
+ def initialize(options={})
55
+ [:nonce_timestamp, :client_credential, :token_credential,
56
+ :temporary_credential, :verifier].each do |attr|
57
+ instance_variable_set("@#{attr}", options[attr])
58
+ end
59
+ end
60
+
61
+ ##
62
+ # Determine if the supplied nonce/timestamp pair is valid by calling
63
+ # the {#nonce_timestamp} Proc.
64
+ #
65
+ # @param [String, #to_str] nonce value from the request
66
+ # @param [String, #to_str] timestamp value from the request
67
+ # @return [Boolean] if the nonce/timestamp pair is valid.
68
+ def validate_nonce_timestamp(nonce, timestamp)
69
+ nonce =
70
+ @nonce_timestamp.call(nonce, timestamp) if
71
+ @nonce_timestamp.respond_to?(:call)
72
+ nonce ? true : false
73
+ end
74
+
75
+ ##
76
+ # Find the appropriate client credential by calling
77
+ # the {#client_credential} Proc.
78
+ #
79
+ # @param [String] key provided to the {#client_credential} Proc.
80
+ # @return [Signet::OAuth1::Credential] The client credential.
81
+ def find_client_credential(key)
82
+ call_credential_lookup(@client_credential, key)
83
+ end
84
+
85
+ ##
86
+ # Find the appropriate client credential by calling
87
+ # the {#token_credential} Proc.
88
+ #
89
+ # @param [String] key provided to the {#token_credential} Proc.
90
+ # @return [Signet::OAuth1::Credential] if the credential is found.
91
+ def find_token_credential(key)
92
+ call_credential_lookup(@token_credential, key)
93
+ end
94
+
95
+ ##
96
+ # Find the appropriate client credential by calling
97
+ # the {#temporary_credential} Proc.
98
+ #
99
+ # @param [String] key provided to the {#temporary_credential} Proc.
100
+ # @return [Signet::OAuth1::Credential] if the credential is found.
101
+ def find_temporary_credential(key)
102
+ call_credential_lookup(@temporary_credential, key)
103
+ end
104
+
105
+ ##
106
+ # Call a credential lookup, and cast the result to a proper Credential.
107
+ #
108
+ # @param [Proc] credential to call.
109
+ # @param [String] key provided to the Proc in <code>credential</code>
110
+ # @return [Signet::OAuth1::Credential] credential provided by
111
+ # <code>credential</code> (if any).
112
+ def call_credential_lookup(credential, key)
113
+ cred = credential.call(key) if
114
+ credential.respond_to?(:call)
115
+ return nil if cred.nil?
116
+ return nil unless (cred.respond_to?(:to_str) ||
117
+ cred.respond_to?(:to_ary) ||
118
+ cred.respond_to?(:to_hash) )
119
+ if(cred.instance_of?(::Signet::OAuth1::Credential))
120
+ cred
121
+ else
122
+ ::Signet::OAuth1::Credential.new(cred)
123
+ end
124
+ end
125
+
126
+ ##
127
+ # Determine if the verifier is valid by calling the Proc in {#verifier}.
128
+ #
129
+ # @param [String] Key provided to the {#verifier} Proc.
130
+ # @return [Boolean] if the verifier Proc returns anything other than
131
+ # <code>nil</code> or <code>false</code>.
132
+ def find_verifier(verifier)
133
+ verified = @verifier.call(verifier) if @verifier.respond_to?(:call)
134
+ verified ? true : false
135
+ end
136
+
137
+
138
+ ##
139
+ # Validate and normalize the components from an HTTP request.
140
+ # @overload verify_request_components(options)
141
+ # @param [Faraday::Request] request A pre-constructed request to verify.
142
+ # @param [String] method the HTTP method , defaults to <code>GET</code>
143
+ # @param [Addressable::URI, String] uri the URI .
144
+ # @param [Hash, Array] headers the HTTP headers.
145
+ # @param [StringIO, String] body The HTTP body.
146
+ # @param [HTTPAdapter] adapter The HTTP adapter(optional).
147
+ # @return [Hash] normalized request components
148
+ def verify_request_components(options={})
149
+ if options[:request]
150
+ if options[:request].kind_of?(Faraday::Request) || options[:request].kind_of?(Array)
151
+ request = options[:request]
152
+ elsif options[:adapter]
153
+ request = options[:adapter].adapt_request(options[:request])
154
+ end
155
+ method = request.method
156
+ uri = request.path
157
+ headers = request.headers
158
+ body = request.body
159
+ else
160
+ method = options[:method] || :get
161
+ uri = options[:uri]
162
+ headers = options[:headers] || []
163
+ body = options[:body] || ''
164
+ end
165
+
166
+ headers = headers.to_a if headers.kind_of?(Hash)
167
+ method = method.to_s.upcase
168
+
169
+ request_components = {
170
+ :method => method,
171
+ :uri => uri,
172
+ :headers => headers
173
+ }
174
+
175
+ # Verify that we have all the pieces required to validate the HTTP request
176
+ request_components.each do |(key, value)|
177
+ unless value
178
+ raise ArgumentError, "Missing :#{key} parameter."
179
+ end
180
+ end
181
+ request_components[:body] = body
182
+ request_components
183
+ end
184
+
185
+ ##
186
+ # Validate and normalize the HTTP Authorization header.
187
+ #
188
+ # @param [Array] headers from HTTP request.
189
+ # @return [Hash] Hash of Authorization header.
190
+ def verify_auth_header_components(headers)
191
+ auth_header = headers.find{|x| x[0] == 'Authorization'}
192
+ if(auth_header.nil? || auth_header[1] == '')
193
+ raise MalformedAuthorizationError.new('Authorization header is missing')
194
+ end
195
+ auth_hash = ::Signet::OAuth1.parse_authorization_header(
196
+ auth_header[1] ).inject({}) {|acc, (key,val)| acc[key.downcase] = val; acc}
197
+
198
+ auth_hash
199
+ end
200
+
201
+
202
+ ##
203
+ # @overload request_realm(options)
204
+ # @param [Hash] request A pre-constructed request to verify.
205
+ # @param [String] method the HTTP method , defaults to <code>GET</code>
206
+ # @param [Addressable::URI, String] uri the URI .
207
+ # @param [Hash, Array] headers the HTTP headers.
208
+ # @param [StringIO, String] body The HTTP body.
209
+ # @param [HTTPAdapter] adapter The HTTP adapter(optional).
210
+ # @return [String] The Authorization realm(see RFC 2617) of the request.
211
+ def request_realm(options={})
212
+ if(options[:request])
213
+ request_components = verify_request_components(
214
+ :request=>options[:request],
215
+ :adapter=>options[:adapter] )
216
+ else
217
+ request_components = verify_request_components(
218
+ :method=>options[:method],
219
+ :uri=>options[:uri],
220
+ :headers=>options[:headers],
221
+ :body=>options[:body] )
222
+ end
223
+
224
+ auth_header = request_components[:headers].find{|x| x[0] == 'Authorization'}
225
+ if(auth_header.nil? || auth_header[1] == '')
226
+ raise MalformedAuthorizationError.new('Authorization header is missing')
227
+ end
228
+ auth_hash = ::Signet::OAuth1.parse_authorization_header(
229
+ auth_header[1] ).inject({}) {|acc, (key,val)| acc[key.downcase] = val; acc}
230
+ auth_hash['realm']
231
+ end
232
+
233
+ ##
234
+ # Authenticates a temporary credential request. If no oauth_callback is
235
+ # present in the request, <code>oob</code> will be returned.
236
+ #
237
+ # @overload authenticate_temporary_credential_request(options)
238
+ # @param [Hash] request The configuration parameters for the request.
239
+ # @param [String] method the HTTP method , defaults to <code>GET</code>
240
+ # @param [Addressable::URI, String] uri the URI .
241
+ # @param [Hash, Array] headers the HTTP headers.
242
+ # @param [StringIO, String] body The HTTP body.
243
+ # @param [HTTPAdapter] adapter The HTTP adapter(optional).
244
+ # @return [String] The oauth_callback value, or <code>false</code> if not valid.
245
+ def authenticate_temporary_credential_request(options={})
246
+ verifications = {
247
+ :client_credential =>
248
+ lambda { |x| ::Signet::OAuth1::Credential.new('Client credential key',
249
+ 'Client credential secret'
250
+ )
251
+ }
252
+ }
253
+ verifications.each do |(key, value)|
254
+ raise ArgumentError, "#{key} was not set." unless self.send(key)
255
+ end
256
+
257
+ if(options[:request])
258
+ request_components = verify_request_components(
259
+ :request=>options[:request],
260
+ :adapter=>options[:adapter] )
261
+ else
262
+ request_components = verify_request_components(
263
+ :method=>options[:method],
264
+ :uri=>options[:uri],
265
+ :headers=>options[:headers] )
266
+ end
267
+ # body should be blank; we don't care in any case.
268
+ method = request_components[:method]
269
+ uri = request_components[:uri]
270
+ headers = request_components[:headers]
271
+
272
+ auth_hash = verify_auth_header_components(headers)
273
+ return false unless(client_credential = find_client_credential(
274
+ auth_hash['oauth_consumer_key']) )
275
+
276
+ return false unless validate_nonce_timestamp(auth_hash['oauth_nonce'],
277
+ auth_hash['oauth_timestamp'])
278
+ client_credential_secret = client_credential.secret if client_credential
279
+
280
+ computed_signature = ::Signet::OAuth1.sign_parameters(
281
+ method,
282
+ uri,
283
+ # Realm isn't used, and will throw the signature off.
284
+ auth_hash.reject{|k,v| k=='realm'}.to_a,
285
+ client_credential_secret,
286
+ nil
287
+ )
288
+ if(computed_signature == auth_hash['oauth_signature'])
289
+ if(auth_hash.fetch('oauth_callback', 'oob').empty?)
290
+ 'oob'
291
+ else
292
+ auth_hash.fetch('oauth_callback')
293
+ end
294
+ else
295
+ false
296
+ end
297
+ end
298
+
299
+
300
+ ##
301
+ # Authenticates a token credential request.
302
+ # @overload authenticate_token_credential_request(options)
303
+ # @param [Hash] request The configuration parameters for the request.
304
+ # @param [String] method the HTTP method , defaults to <code>GET</code>
305
+ # @param [Addressable::URI, String] uri the URI .
306
+ # @param [Hash, Array] headers the HTTP headers.
307
+ # @param [StringIO, String] body The HTTP body.
308
+ # @param [HTTPAdapter] adapter The HTTP adapter(optional).
309
+ # @return [Hash] A hash of credentials and realm for a valid request,
310
+ # or <code>nil</code> if not valid.
311
+ def authenticate_token_credential_request(options={})
312
+ verifications = {
313
+ :client_credential =>
314
+ lambda {|x| ::Signet::OAuth1::Credential.new('Client credential key',
315
+ 'Client credential secret')
316
+ },
317
+ :temporary_credential =>
318
+ lambda {|x| ::Signet::OAuth1::Credential.new('Temporary credential key',
319
+ 'Temporary credential secret')
320
+ },
321
+ :verifier =>
322
+ lambda {|x| 'Verifier' }
323
+ }
324
+ verifications.each do |(key, value)|
325
+ unless self.send(key)
326
+ raise ArgumentError, "#{key} was not set."
327
+ end
328
+ end
329
+ if(options[:request])
330
+ request_components = verify_request_components(
331
+ :request=>options[:request],
332
+ :adapter=>options[:adapter]
333
+ )
334
+ else
335
+ request_components = verify_request_components(
336
+ :method=>options[:method],
337
+ :uri=>options[:uri],
338
+ :headers=>options[:headers],
339
+ :body=>options[:body]
340
+ )
341
+ end
342
+ # body should be blank; we don't care in any case.
343
+ method = request_components[:method]
344
+ uri = request_components[:uri]
345
+ headers = request_components[:headers]
346
+
347
+ auth_hash = verify_auth_header_components(headers)
348
+ return false unless(
349
+ client_credential = find_client_credential(auth_hash['oauth_consumer_key'])
350
+ )
351
+ return false unless(
352
+ temporary_credential = find_temporary_credential(auth_hash['oauth_token'])
353
+ )
354
+ return false unless validate_nonce_timestamp(
355
+ auth_hash['oauth_nonce'], auth_hash['oauth_timestamp'])
356
+
357
+ computed_signature = ::Signet::OAuth1.sign_parameters(
358
+ method,
359
+ uri,
360
+ # Realm isn't used, and will throw the signature off.
361
+ auth_hash.reject{|k,v| k=='realm'}.to_a,
362
+ client_credential.secret,
363
+ temporary_credential.secret
364
+ )
365
+
366
+ if(computed_signature == auth_hash['oauth_signature'])
367
+ {:client_credential=>client_credential,
368
+ :temporary_credential=>temporary_credential,
369
+ :realm=>auth_hash['realm']
370
+ }
371
+ else
372
+ nil
373
+ end
374
+ end
375
+
376
+ ##
377
+ # Authenticates a request for a protected resource.
378
+ # @overload authenticate_resource_request(options)
379
+ # @param [Hash] request The configuration parameters for the request.
380
+ # @param [String] method the HTTP method , defaults to <code>GET</code>
381
+ # @param [Addressable::URI, String] uri the URI .
382
+ # @param [Hash, Array] headers the HTTP headers.
383
+ # @param [StringIO, String] body The HTTP body.
384
+ # @param [Boolean] two_legged skip the token_credential lookup?
385
+ # @param [HTTPAdapter] adapter The HTTP adapter(optional).
386
+ #
387
+ # @return [Hash] A hash of the credentials and realm for a valid request,
388
+ # or <code>nil</code> if not valid.
389
+ def authenticate_resource_request(options={})
390
+ verifications = {
391
+ :client_credential =>
392
+ lambda do |x|
393
+ ::Signet::OAuth1::Credential.new('Client credential key',
394
+ 'Client credential secret')
395
+ end
396
+ }
397
+
398
+ unless(options[:two_legged] == true)
399
+ verifications.update(
400
+ :token_credential =>
401
+ lambda do |x|
402
+ ::Signet::OAuth1::Credential.new('Token credential key',
403
+ 'Token credential secret')
404
+ end
405
+ )
406
+ end
407
+ # Make sure all required state is set
408
+ verifications.each do |(key, value)|
409
+ unless self.send(key)
410
+ raise ArgumentError, "#{key} was not set."
411
+ end
412
+ end
413
+
414
+ if(options[:request])
415
+ request_components = verify_request_components(
416
+ :request=>options[:request],
417
+ :adapter=>options[:adapter] )
418
+ else
419
+ request_components = verify_request_components(
420
+ :method=>options[:method],
421
+ :uri=>options[:uri],
422
+ :headers=>options[:headers],
423
+ :body=>options[:body] )
424
+ end
425
+ method = request_components[:method]
426
+ uri = request_components[:uri]
427
+ headers = request_components[:headers]
428
+ body = request_components[:body]
429
+
430
+
431
+ if !body.kind_of?(String) && body.respond_to?(:each)
432
+ # Just in case we get a chunked body
433
+ merged_body = StringIO.new
434
+ body.each do |chunk|
435
+ merged_body.write(chunk)
436
+ end
437
+ body = merged_body.string
438
+ end
439
+ if !body.kind_of?(String)
440
+ raise TypeError, "Expected String, got #{body.class}."
441
+ end
442
+
443
+ media_type = nil
444
+ headers.each do |(header, value)|
445
+ if header.downcase == 'Content-Type'.downcase
446
+ media_type = value.gsub(/^([^;]+)(;.*?)?$/, '\1')
447
+ end
448
+ end
449
+
450
+ auth_hash = verify_auth_header_components(headers)
451
+
452
+ auth_token = auth_hash['oauth_token']
453
+
454
+
455
+ unless(options[:two_legged])
456
+ return nil if(auth_token.nil?)
457
+ return nil unless(token_credential = find_token_credential(auth_token))
458
+ token_credential_secret = token_credential.secret if token_credential
459
+ end
460
+
461
+ return nil unless(client_credential =
462
+ find_client_credential(auth_hash['oauth_consumer_key']))
463
+
464
+ return nil unless validate_nonce_timestamp(auth_hash['oauth_nonce'],
465
+ auth_hash['oauth_timestamp'])
466
+
467
+ if(method == ('POST' || 'PUT') &&
468
+ media_type == 'application/x-www-form-urlencoded')
469
+ request_components[:body] = body
470
+ post_parameters = Addressable::URI.form_unencode(body)
471
+ post_parameters.each {|param| param[1] = "" if param[1].nil?}
472
+ # If the auth header doesn't have the same params as the body, it
473
+ # can't have been signed correctly(5849#3.4.1.3)
474
+ unless(post_parameters.sort == auth_hash.reject{|k,v| k.index('oauth_')}.to_a.sort)
475
+ raise MalformedAuthorizationError.new(
476
+ 'Request is of type application/x-www-form-urlencoded ' +
477
+ 'but Authentication header did not include form values'
478
+ )
479
+ end
480
+ end
481
+
482
+ client_credential_secret = client_credential.secret if client_credential
483
+
484
+ computed_signature = ::Signet::OAuth1.sign_parameters(
485
+ method,
486
+ uri,
487
+ # Realm isn't used, and will throw the signature off.
488
+ auth_hash.reject{|k,v| k=='realm'}.to_a,
489
+ client_credential_secret,
490
+ token_credential_secret
491
+ )
492
+
493
+ if(computed_signature == auth_hash['oauth_signature'])
494
+ {:client_credential=>client_credential,
495
+ :token_credential=>token_credential,
496
+ :realm=>auth_hash['realm']
497
+ }
498
+ else
499
+ nil
500
+ end
501
+ end
502
+
503
+ end
504
+ end
505
+ end