mojodna-oauth 0.3.4 → 0.3.4.1

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/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