mojodna-oauth 0.3.4 → 0.3.4.1

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt CHANGED
@@ -1,3 +1,11 @@
1
+ == 0.3.5
2
+
3
+ * `query` CLI command to access protected resources (Seth)
4
+ * Added -H, -Q CLI options for specifying the authorization scheme (Seth)
5
+ * Added -O CLI option for specifying a file containing options (Seth)
6
+ * Support streamable body contents for large request bodies (Seth Cousins)
7
+ * Support for OAuth 1.0a (Seth)
8
+
1
9
  == 0.3.4 2009-05-06
2
10
 
3
11
  * OAuth::Client::Helper uses OAuth::VERSION (chadisfaction)
data/examples/yql.rb CHANGED
@@ -39,6 +39,6 @@ consumer = OAuth::Consumer.new \
39
39
 
40
40
  access_token = OAuth::AccessToken.new(consumer)
41
41
 
42
- response = access_token.request(:get, url = "/v1/yql?q=#{OAuth::Helper.escape(query)}&format=json")
42
+ response = access_token.request(:get, "/v1/yql?q=#{OAuth::Helper.escape(query)}&format=json")
43
43
  rsp = JSON.parse(response.body)
44
44
  pp rsp
data/lib/oauth/cli.rb CHANGED
@@ -6,6 +6,7 @@ module OAuth
6
6
  SUPPORTED_COMMANDS = {
7
7
  "authorize" => "Obtain an access token and secret for a user",
8
8
  "debug" => "Verbosely generate an OAuth signature",
9
+ "query" => "Query a protected resource",
9
10
  "sign" => "Generate an OAuth signature"
10
11
  }
11
12
 
@@ -41,38 +42,59 @@ module OAuth
41
42
  case command
42
43
  # TODO move command logic elsewhere
43
44
  when "authorize"
44
- # Y! token authority requires realm=yahoo.com when headers are in use
45
- # TODO remove :scheme when that's been fixed
46
- # TODO determine endpoints w/ X-RDS-Simple
47
45
  consumer = OAuth::Consumer.new \
48
46
  options[:oauth_consumer_key],
49
47
  options[:oauth_consumer_secret],
50
48
  :access_token_url => options[:access_token_url],
51
49
  :authorize_url => options[:authorize_url],
52
50
  :request_token_url => options[:request_token_url],
53
- :scheme => :query_string
51
+ :scheme => options[:scheme]
52
+
53
+ # parameters for OAuth 1.0a
54
+ oauth_verifier = nil
54
55
 
55
56
  # get a request token
56
- request_token = consumer.get_request_token
57
+ request_token = consumer.get_request_token(:oauth_callback => options[:oauth_callback])
58
+
59
+ if request_token.callback_confirmed?
60
+ stdout.puts "Server appears to support OAuth 1.0a; enabling support."
61
+ options[:version] = "1.0a"
62
+ end
57
63
 
58
64
  stdout.puts "Please visit this url to authorize:"
59
65
  stdout.puts request_token.authorize_url
60
66
 
61
- stdout.puts "Press return to continue..."
62
- stdin.gets
67
+ if options[:version] == "1.0a"
68
+ stdout.puts "Please enter the verification code provided by the SP (oauth_verifier):"
69
+ oauth_verifier = stdin.gets.chomp
70
+ else
71
+ stdout.puts "Press return to continue..."
72
+ stdin.gets
73
+ end
63
74
 
64
75
  begin
65
76
  # get an access token
66
- access_token = request_token.get_access_token
77
+ access_token = request_token.get_access_token(:oauth_verifier => oauth_verifier)
67
78
 
68
79
  stdout.puts "Response:"
69
80
  access_token.params.each do |k,v|
70
- stdout.puts " #{k}: #{v}"
81
+ stdout.puts " #{k}: #{v}" unless k.is_a?(Symbol)
71
82
  end
72
83
  rescue OAuth::Unauthorized => e
73
84
  stderr.puts "A problem occurred while attempting to obtain an access token:"
74
85
  stderr.puts e
75
86
  end
87
+ when "query"
88
+ consumer = OAuth::Consumer.new \
89
+ options[:oauth_consumer_key],
90
+ options[:oauth_consumer_secret],
91
+ :scheme => options[:scheme]
92
+
93
+ access_token = OAuth::AccessToken.new(consumer, options[:oauth_token], options[:oauth_token_secret])
94
+
95
+ response = access_token.request(options[:method].downcase.to_sym, options[:uri])
96
+ puts "#{response.code} #{response.message}"
97
+ puts response.body
76
98
  when "sign"
77
99
  parameters = prepare_parameters
78
100
 
@@ -147,7 +169,9 @@ module OAuth
147
169
  parse_options(arguments[0..-1])
148
170
  end
149
171
 
150
- def option_parser
172
+ def option_parser(arguments)
173
+ # TODO add realm parameter
174
+ # TODO add user-agent parameter
151
175
  option_parser = OptionParser.new do |opts|
152
176
  opts.banner = "Usage: #{$0} [options] <command>"
153
177
 
@@ -157,6 +181,8 @@ module OAuth
157
181
  options[:oauth_timestamp] = OAuth::Helper.generate_timestamp
158
182
  options[:oauth_version] = "1.0"
159
183
  options[:params] = []
184
+ options[:scheme] = :header
185
+ options[:version] = "1.0"
160
186
 
161
187
  ## Common Options
162
188
 
@@ -168,7 +194,19 @@ module OAuth
168
194
  options[:oauth_consumer_secret] = v
169
195
  end
170
196
 
171
- ## Options for signing
197
+ opts.on("-H", "--header", "Use the 'Authorization' header for OAuth parameters (default).") do
198
+ options[:scheme] = :header
199
+ end
200
+
201
+ opts.on("-Q", "--query-string", "Use the query string for OAuth parameters.") do
202
+ options[:scheme] = :query_string
203
+ end
204
+
205
+ opts.on("-O", "--options FILE", "Read options from a file") do |v|
206
+ arguments.unshift(*open(v).readlines.map { |l| l.chomp.split(" ") }.flatten)
207
+ end
208
+
209
+ ## Options for signing and making requests
172
210
 
173
211
  opts.on("--method METHOD", "Specifies the method (e.g. GET) to use when signing.") do |v|
174
212
  options[:method] = v
@@ -233,6 +271,10 @@ module OAuth
233
271
  options[:authorize_url] = v
234
272
  end
235
273
 
274
+ opts.on("--callback-url URL", "Specifies a callback URL.") do |v|
275
+ options[:oauth_callback] = v
276
+ end
277
+
236
278
  opts.on("--request-token-url URL", "Specifies the request token URL.") do |v|
237
279
  options[:request_token_url] = v
238
280
  end
@@ -240,7 +282,7 @@ module OAuth
240
282
  end
241
283
 
242
284
  def parse_options(arguments)
243
- option_parser.parse!(arguments)
285
+ option_parser(arguments).parse!(arguments)
244
286
  end
245
287
 
246
288
  def prepare_parameters
@@ -29,11 +29,13 @@ module OAuth::Client
29
29
 
30
30
  def oauth_parameters
31
31
  {
32
+ 'oauth_callback' => options[:oauth_callback],
32
33
  'oauth_consumer_key' => options[:consumer].key,
33
34
  'oauth_token' => options[:token] ? options[:token].token : '',
34
35
  'oauth_signature_method' => options[:signature_method],
35
36
  'oauth_timestamp' => timestamp,
36
37
  'oauth_nonce' => nonce,
38
+ 'oauth_verifier' => options[:oauth_verifier],
37
39
  'oauth_version' => '1.0'
38
40
  }.reject { |k,v| v.to_s == "" }
39
41
  end
@@ -11,6 +11,12 @@ class Net::HTTPRequest
11
11
  # this may add a header, additional query string parameters, or additional POST body parameters.
12
12
  # The default scheme is +header+, in which the OAuth parameters as put into the +Authorization+
13
13
  # header.
14
+ #
15
+ # * http - Configured Net::HTTP instance
16
+ # * consumer - OAuth::Consumer instance
17
+ # * token - OAuth::Token instance
18
+ # * options - Request-specific options (e.g. +request_uri+, +consumer+, +token+, +scheme+,
19
+ # +signature_method+, +nonce+, +timestamp+)
14
20
  #
15
21
  # This method also modifies the <tt>User-Agent</tt> header to add the OAuth gem version.
16
22
  #
@@ -34,7 +40,13 @@ class Net::HTTPRequest
34
40
  # on the <tt>options[:scheme]</tt> being used so this must match what will be used for the request
35
41
  # itself. The default scheme is +header+, in which the OAuth parameters as put into the +Authorization+
36
42
  # header.
37
- #
43
+ #
44
+ # * http - Configured Net::HTTP instance
45
+ # * consumer - OAuth::Consumer instance
46
+ # * token - OAuth::Token instance
47
+ # * options - Request-specific options (e.g. +request_uri+, +consumer+, +token+, +scheme+,
48
+ # +signature_method+, +nonce+, +timestamp+)
49
+ #
38
50
  # See Also: {OAuth core spec version 1.0, section 9.1.1}[http://oauth.net/core/1.0#rfc.section.9.1.1]
39
51
  def signature_base_string(http, consumer = nil, token = nil, options = {})
40
52
  options = { :request_uri => oauth_full_request_uri(http),
@@ -97,18 +97,40 @@ module OAuth
97
97
  end
98
98
  end
99
99
 
100
+ def get_access_token(request_token, request_options = {}, *arguments)
101
+ response = token_request(http_method, (access_token_url? ? access_token_url : access_token_path), request_token, request_options, *arguments)
102
+ OAuth::AccessToken.from_hash(self, response)
103
+ end
104
+
100
105
  # Makes a request to the service for a new OAuth::RequestToken
101
106
  #
102
107
  # @request_token = @consumer.get_request_token
103
108
  #
109
+ # To include OAuth parameters:
110
+ #
111
+ # @request_token = @consumer.get_request_token \
112
+ # :oauth_callback => "http://example.com/cb"
113
+ #
114
+ # To include application-specific parameters:
115
+ #
116
+ # @request_token = @consumer.get_request_token({}, :foo => "bar")
117
+ #
118
+ # TODO oauth_callback should be a mandatory parameter
104
119
  def get_request_token(request_options = {}, *arguments)
120
+ # if oauth_callback wasn't provided, it is assumed that oauth_verifiers
121
+ # will be exchanged out of band
122
+ request_options[:oauth_callback] ||= OAuth::OUT_OF_BAND
123
+
105
124
  response = token_request(http_method, (request_token_url? ? request_token_url : request_token_path), nil, request_options, *arguments)
106
- OAuth::RequestToken.new(self, response[:oauth_token], response[:oauth_token_secret])
125
+ OAuth::RequestToken.from_hash(self, response)
107
126
  end
108
127
 
109
128
  # Creates, signs and performs an http request.
110
129
  # It's recommended to use the OAuth::Token classes to set this up correctly.
111
- # The arguments parameters are a hash or string encoded set of parameters if it's a post request as well as optional http headers.
130
+ # request_options take precedence over consumer-wide options when signing
131
+ # a request.
132
+ # arguments are POST and PUT bodies (a Hash, string-encoded parameters, or
133
+ # absent), followed by additional HTTP headers.
112
134
  #
113
135
  # @consumer.request(:get, '/people', @token, { :scheme => :query_string })
114
136
  # @consumer.request(:post, '/people', @token, {}, @person.to_xml, { 'Content-Type' => 'application/xml' })
@@ -159,7 +181,14 @@ module OAuth
159
181
  case response.code.to_i
160
182
 
161
183
  when (200..299)
162
- CGI.parse(response.body).inject({}) { |h,(k,v)| h[k.to_sym] = v.first; h }
184
+ # symbolize keys
185
+ # TODO this could be considered unexpected behavior; symbols or not?
186
+ # TODO this also drops subsequent values from multi-valued keys
187
+ CGI.parse(response.body).inject({}) do |h,(k,v)|
188
+ h[k.to_sym] = v.first
189
+ h[k] = v.first
190
+ h
191
+ end
163
192
  when (300..399)
164
193
  # this is a redirect
165
194
  response.error!
@@ -280,8 +309,19 @@ module OAuth
280
309
  if data.is_a?(Hash)
281
310
  request.set_form_data(data)
282
311
  elsif data
283
- request.body = data.to_s
284
- request["Content-Length"] = request.body.length
312
+ if data.respond_to?(:read)
313
+ request.body_stream = data
314
+ if data.respond_to?(:length)
315
+ request["Content-Length"] = data.length
316
+ elsif data.respond_to?(:stat) && data.stat.respond_to?(:size)
317
+ request["Content-Length"] = data.stat.size
318
+ else
319
+ raise ArgumentError, "Don't know how to send a body_stream that doesn't respond to .length or .stat.size"
320
+ end
321
+ else
322
+ request.body = data.to_s
323
+ request["Content-Length"] = request.body.length
324
+ end
285
325
  end
286
326
 
287
327
  request
data/lib/oauth/oauth.rb CHANGED
@@ -1,6 +1,10 @@
1
1
  module OAuth
2
+ # request tokens are passed between the consumer and the provider out of
3
+ # band (i.e. callbacks cannot be used), per section 6.1.1
4
+ OUT_OF_BAND = "oob"
5
+
2
6
  # required parameters, per sections 6.1.1, 6.3.1, and 7
3
- PARAMETERS = %w(oauth_consumer_key oauth_token oauth_signature_method oauth_timestamp oauth_nonce oauth_version oauth_signature)
7
+ PARAMETERS = %w(oauth_callback oauth_consumer_key oauth_token oauth_signature_method oauth_timestamp oauth_nonce oauth_verifier oauth_version oauth_signature)
4
8
 
5
9
  # reserved character regexp, per section 5.1
6
10
  RESERVED_CHARACTERS = /[^a-zA-Z0-9\-\.\_\~]/
@@ -18,6 +18,10 @@ module OAuth::RequestProxy
18
18
 
19
19
  ## OAuth parameters
20
20
 
21
+ def oauth_callback
22
+ parameters['oauth_callback']
23
+ end
24
+
21
25
  def oauth_consumer_key
22
26
  parameters['oauth_consumer_key']
23
27
  end
@@ -48,6 +52,10 @@ module OAuth::RequestProxy
48
52
  parameters['oauth_token']
49
53
  end
50
54
 
55
+ def oauth_verifier
56
+ parameters['oauth_verifier']
57
+ end
58
+
51
59
  def oauth_version
52
60
  parameters["oauth_version"]
53
61
  end
@@ -13,6 +13,7 @@ module OAuth
13
13
  def initialize(consumer, token="", secret="")
14
14
  super(token, secret)
15
15
  @consumer = consumer
16
+ @params = {}
16
17
  end
17
18
 
18
19
  # Make a signed request using given http_method to the path
@@ -9,6 +9,10 @@ module OAuth
9
9
  build_authorize_url(consumer.authorize_url, params)
10
10
  end
11
11
 
12
+ def callback_confirmed?
13
+ params[:oauth_callback_confirmed] == "true"
14
+ end
15
+
12
16
  # exchange for AccessToken on server
13
17
  def get_access_token(options = {}, *arguments)
14
18
  response = consumer.token_request(consumer.http_method, (consumer.access_token_url? ? consumer.access_token_url : consumer.access_token_path), self, options, *arguments)
data/lib/oauth/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module OAuth #:nodoc:
2
- VERSION = '0.3.4'
2
+ VERSION = '0.3.4.1'
3
3
  end
data/oauth.gemspec CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = %q{oauth}
5
- s.version = "0.3.4"
5
+ s.version = "0.3.4.1"
6
6
 
7
7
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
8
  s.authors = ["Pelle Braendgaard", "Blaine Cook", "Larry Halff", "Jesse Clark", "Jon Crosby", "Seth Fitzsimmons", "Matt Sanford"]
@@ -1,6 +1,7 @@
1
1
  require File.dirname(__FILE__) + '/test_helper'
2
2
  require 'oauth/consumer'
3
3
  require 'oauth/signature/rsa/sha1'
4
+ require 'stringio'
4
5
 
5
6
 
6
7
  # This performs testing against Andy Smith's test server http://term.ie/oauth/example/
@@ -318,10 +319,43 @@ class ConsumerTest < Test::Unit::TestCase
318
319
  debug)
319
320
  end
320
321
 
322
+ def test_post_with_body_stream
323
+ @consumer=OAuth::Consumer.new(
324
+ "key",
325
+ "secret",
326
+ {
327
+ :site=>"http://term.ie",
328
+ :request_token_path=>"/oauth/example/request_token.php",
329
+ :access_token_path=>"/oauth/example/access_token.php",
330
+ :authorize_path=>"/oauth/example/authorize.php"
331
+ })
332
+
333
+
334
+ @request_token=@consumer.get_request_token
335
+ @access_token=@request_token.get_access_token
336
+
337
+ request_body_string = "Hello, hello, hello"
338
+ request_body_stream = StringIO.new( request_body_string )
339
+
340
+ @response=@access_token.post("/oauth/example/echo_api.php",request_body_stream)
341
+ assert_not_nil @response
342
+ assert_equal "200",@response.code
343
+
344
+ request_body_file = File.open(__FILE__)
345
+
346
+ @response=@access_token.post("/oauth/example/echo_api.php",request_body_file)
347
+ assert_not_nil @response
348
+ assert_equal "200",@response.code
349
+
350
+ # unfortunately I don't know of a way to test that the body data was received correctly since the test server at http://term.ie
351
+ # echos back any non-oauth parameters but not the body. However, this does test that the request is still correctly signed
352
+ # (including the Content-Length header) and that the server received Content-Length bytes of body since it won't process the
353
+ # request & respond until the full body length is received.
354
+ end
355
+
321
356
  protected
322
357
 
323
358
  def request_parameters_to_s
324
359
  @request_parameters.map { |k,v| "#{k}=#{v}" }.join("&")
325
360
  end
326
-
327
361
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mojodna-oauth
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.4
4
+ version: 0.3.4.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Pelle Braendgaard