af-oauth 0.3.4.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (85) hide show
  1. data/History.txt +83 -0
  2. data/License.txt +20 -0
  3. data/Manifest.txt +84 -0
  4. data/README.rdoc +71 -0
  5. data/Rakefile +36 -0
  6. data/TODO +31 -0
  7. data/bin/oauth +5 -0
  8. data/examples/yql.rb +44 -0
  9. data/lib/oauth.rb +4 -0
  10. data/lib/oauth/cli.rb +300 -0
  11. data/lib/oauth/client.rb +4 -0
  12. data/lib/oauth/client/action_controller_request.rb +54 -0
  13. data/lib/oauth/client/helper.rb +81 -0
  14. data/lib/oauth/client/net_http.rb +94 -0
  15. data/lib/oauth/consumer.rb +297 -0
  16. data/lib/oauth/errors.rb +3 -0
  17. data/lib/oauth/errors/error.rb +4 -0
  18. data/lib/oauth/errors/problem.rb +14 -0
  19. data/lib/oauth/errors/unauthorized.rb +12 -0
  20. data/lib/oauth/helper.rb +77 -0
  21. data/lib/oauth/oauth.rb +7 -0
  22. data/lib/oauth/oauth_test_helper.rb +25 -0
  23. data/lib/oauth/request_proxy.rb +24 -0
  24. data/lib/oauth/request_proxy/action_controller_request.rb +63 -0
  25. data/lib/oauth/request_proxy/base.rb +159 -0
  26. data/lib/oauth/request_proxy/jabber_request.rb +41 -0
  27. data/lib/oauth/request_proxy/mock_request.rb +44 -0
  28. data/lib/oauth/request_proxy/net_http.rb +65 -0
  29. data/lib/oauth/request_proxy/rack_request.rb +40 -0
  30. data/lib/oauth/server.rb +66 -0
  31. data/lib/oauth/signature.rb +37 -0
  32. data/lib/oauth/signature/base.rb +99 -0
  33. data/lib/oauth/signature/hmac/base.rb +12 -0
  34. data/lib/oauth/signature/hmac/md5.rb +9 -0
  35. data/lib/oauth/signature/hmac/rmd160.rb +9 -0
  36. data/lib/oauth/signature/hmac/sha1.rb +9 -0
  37. data/lib/oauth/signature/hmac/sha2.rb +9 -0
  38. data/lib/oauth/signature/md5.rb +13 -0
  39. data/lib/oauth/signature/plaintext.rb +23 -0
  40. data/lib/oauth/signature/rsa/sha1.rb +45 -0
  41. data/lib/oauth/signature/sha1.rb +13 -0
  42. data/lib/oauth/token.rb +7 -0
  43. data/lib/oauth/tokens/access_token.rb +68 -0
  44. data/lib/oauth/tokens/consumer_token.rb +32 -0
  45. data/lib/oauth/tokens/request_token.rb +28 -0
  46. data/lib/oauth/tokens/server_token.rb +9 -0
  47. data/lib/oauth/tokens/token.rb +17 -0
  48. data/lib/oauth/version.rb +3 -0
  49. data/oauth.gemspec +49 -0
  50. data/script/destroy +14 -0
  51. data/script/generate +14 -0
  52. data/script/txt2html +74 -0
  53. data/setup.rb +1585 -0
  54. data/tasks/deployment.rake +34 -0
  55. data/tasks/environment.rake +7 -0
  56. data/tasks/website.rake +17 -0
  57. data/test/cases/oauth_case.rb +19 -0
  58. data/test/cases/spec/1_0-final/test_construct_request_url.rb +62 -0
  59. data/test/cases/spec/1_0-final/test_normalize_request_parameters.rb +88 -0
  60. data/test/cases/spec/1_0-final/test_parameter_encodings.rb +86 -0
  61. data/test/cases/spec/1_0-final/test_signature_base_strings.rb +77 -0
  62. data/test/keys/rsa.cert +11 -0
  63. data/test/keys/rsa.pem +16 -0
  64. data/test/test_access_token.rb +28 -0
  65. data/test/test_action_controller_request_proxy.rb +127 -0
  66. data/test/test_consumer.rb +327 -0
  67. data/test/test_helper.rb +10 -0
  68. data/test/test_hmac_sha1.rb +21 -0
  69. data/test/test_net_http_client.rb +187 -0
  70. data/test/test_net_http_request_proxy.rb +73 -0
  71. data/test/test_oauth_helper.rb +50 -0
  72. data/test/test_rack_request_proxy.rb +40 -0
  73. data/test/test_request_token.rb +53 -0
  74. data/test/test_rsa_sha1.rb +59 -0
  75. data/test/test_server.rb +40 -0
  76. data/test/test_signature.rb +19 -0
  77. data/test/test_signature_base.rb +32 -0
  78. data/test/test_signature_plain_text.rb +31 -0
  79. data/test/test_token.rb +14 -0
  80. data/website/index.html +87 -0
  81. data/website/index.txt +73 -0
  82. data/website/javascripts/rounded_corners_lite.inc.js +285 -0
  83. data/website/stylesheets/screen.css +138 -0
  84. data/website/template.rhtml +48 -0
  85. metadata +241 -0
@@ -0,0 +1,4 @@
1
+ module OAuth
2
+ module Client
3
+ end
4
+ end
@@ -0,0 +1,54 @@
1
+ require 'oauth/client/helper'
2
+ require 'oauth/request_proxy/action_controller_request'
3
+ require 'action_controller/test_process'
4
+
5
+ module ActionController
6
+ class Base
7
+ def process_with_oauth(request, response=nil)
8
+ request.apply_oauth!
9
+ process_without_oauth(request, response)
10
+ end
11
+
12
+ alias_method_chain :process, :oauth
13
+ end
14
+
15
+ class TestRequest
16
+ def self.use_oauth=(bool)
17
+ @use_oauth = bool
18
+ end
19
+
20
+ def self.use_oauth?
21
+ @use_oauth
22
+ end
23
+
24
+ def configure_oauth(consumer = nil, token = nil, options = {})
25
+ @oauth_options = { :consumer => consumer,
26
+ :token => token,
27
+ :scheme => 'header',
28
+ :signature_method => nil,
29
+ :nonce => nil,
30
+ :timestamp => nil }.merge(options)
31
+ end
32
+
33
+ def apply_oauth!
34
+ return unless ActionController::TestRequest.use_oauth? && @oauth_options
35
+
36
+ @oauth_helper = OAuth::Client::Helper.new(self, @oauth_options.merge(:request_uri => request_uri))
37
+ @oauth_helper.amend_user_agent_header(env)
38
+
39
+ self.send("set_oauth_#{@oauth_options[:scheme]}")
40
+ end
41
+
42
+ def set_oauth_header
43
+ env['Authorization'] = @oauth_helper.header
44
+ end
45
+
46
+ def set_oauth_parameters
47
+ @query_parameters = @oauth_helper.parameters_with_oauth
48
+ @query_parameters.merge!(:oauth_signature => @oauth_helper.signature)
49
+ end
50
+
51
+ def set_oauth_query_string
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,81 @@
1
+ require 'oauth/client'
2
+ require 'oauth/consumer'
3
+ require 'oauth/helper'
4
+ require 'oauth/token'
5
+ require 'oauth/version'
6
+ require 'oauth/signature/hmac/sha1'
7
+
8
+ module OAuth::Client
9
+ class Helper
10
+ include OAuth::Helper
11
+
12
+ def initialize(request, options = {})
13
+ @request = request
14
+ @options = options
15
+ @options[:signature_method] ||= 'HMAC-SHA1'
16
+ end
17
+
18
+ def options
19
+ @options
20
+ end
21
+
22
+ def nonce
23
+ options[:nonce] ||= generate_key
24
+ end
25
+
26
+ def timestamp
27
+ options[:timestamp] ||= generate_timestamp
28
+ end
29
+
30
+ def oauth_parameters
31
+ {
32
+ 'oauth_consumer_key' => options[:consumer].key,
33
+ 'oauth_token' => options[:token] ? options[:token].token : '',
34
+ 'oauth_signature_method' => options[:signature_method],
35
+ 'oauth_timestamp' => timestamp,
36
+ 'oauth_nonce' => nonce,
37
+ 'oauth_version' => '1.0'
38
+ }.reject { |k,v| v.to_s == "" }
39
+ end
40
+
41
+ def signature(extra_options = {})
42
+ OAuth::Signature.sign(@request, { :uri => options[:request_uri],
43
+ :consumer => options[:consumer],
44
+ :token => options[:token] }.merge(extra_options) )
45
+ end
46
+
47
+ def signature_base_string(extra_options = {})
48
+ OAuth::Signature.signature_base_string(@request, { :uri => options[:request_uri],
49
+ :consumer => options[:consumer],
50
+ :token => options[:token],
51
+ :parameters => oauth_parameters}.merge(extra_options) )
52
+ end
53
+
54
+ def amend_user_agent_header(headers)
55
+ @oauth_ua_string ||= "OAuth gem v#{OAuth::VERSION}"
56
+ if headers['User-Agent']
57
+ headers['User-Agent'] += " (#{@oauth_ua_string})"
58
+ else
59
+ headers['User-Agent'] = @oauth_ua_string
60
+ end
61
+ end
62
+
63
+ def header
64
+ parameters = oauth_parameters
65
+ parameters.merge!('oauth_signature' => signature(options.merge(:parameters => parameters)))
66
+
67
+ header_params_str = parameters.map { |k,v| "#{k}=\"#{escape(v)}\"" }.join(', ')
68
+
69
+ realm = "realm=\"#{options[:realm]}\", " if options[:realm]
70
+ "OAuth #{realm}#{header_params_str}"
71
+ end
72
+
73
+ def parameters
74
+ OAuth::RequestProxy.proxy(@request).parameters
75
+ end
76
+
77
+ def parameters_with_oauth
78
+ oauth_parameters.merge(parameters)
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,94 @@
1
+ require 'oauth/helper'
2
+ require 'oauth/client/helper'
3
+ require 'oauth/request_proxy/net_http'
4
+
5
+ class Net::HTTPRequest
6
+ include OAuth::Helper
7
+
8
+ attr_reader :oauth_helper
9
+
10
+ # Add the OAuth information to an HTTP request. Depending on the <tt>options[:scheme]</tt> setting
11
+ # this may add a header, additional query string parameters, or additional POST body parameters.
12
+ # The default scheme is +header+, in which the OAuth parameters as put into the +Authorization+
13
+ # header.
14
+ #
15
+ # This method also modifies the <tt>User-Agent</tt> header to add the OAuth gem version.
16
+ #
17
+ # See Also: {OAuth core spec version 1.0, section 5.4.1}[http://oauth.net/core/1.0#rfc.section.5.4.1]
18
+ def oauth!(http, consumer = nil, token = nil, options = {})
19
+ options = { :request_uri => oauth_full_request_uri(http),
20
+ :consumer => consumer,
21
+ :token => token,
22
+ :scheme => 'header',
23
+ :signature_method => nil,
24
+ :nonce => nil,
25
+ :timestamp => nil }.merge(options)
26
+
27
+ @oauth_helper = OAuth::Client::Helper.new(self, options)
28
+ @oauth_helper.amend_user_agent_header(self)
29
+ self.send("set_oauth_#{options[:scheme]}")
30
+ end
31
+
32
+ # Create a string suitable for signing for an HTTP request. This process involves parameter
33
+ # normalization as specified in the OAuth specification. The exact normalization also depends
34
+ # on the <tt>options[:scheme]</tt> being used so this must match what will be used for the request
35
+ # itself. The default scheme is +header+, in which the OAuth parameters as put into the +Authorization+
36
+ # header.
37
+ #
38
+ # See Also: {OAuth core spec version 1.0, section 9.1.1}[http://oauth.net/core/1.0#rfc.section.9.1.1]
39
+ def signature_base_string(http, consumer = nil, token = nil, options = {})
40
+ options = { :request_uri => oauth_full_request_uri(http),
41
+ :consumer => consumer,
42
+ :token => token,
43
+ :scheme => 'header',
44
+ :signature_method => nil,
45
+ :nonce => nil,
46
+ :timestamp => nil }.merge(options)
47
+
48
+ OAuth::Client::Helper.new(self, options).signature_base_string
49
+ end
50
+
51
+ private
52
+
53
+ def oauth_full_request_uri(http)
54
+ uri = URI.parse(self.path)
55
+ uri.host = http.address
56
+ uri.port = http.port
57
+
58
+ if http.respond_to?(:use_ssl?) && http.use_ssl?
59
+ uri.scheme = "https"
60
+ else
61
+ uri.scheme = "http"
62
+ end
63
+
64
+ uri.to_s
65
+ end
66
+
67
+ def set_oauth_header
68
+ self['Authorization'] = @oauth_helper.header
69
+ end
70
+
71
+ # FIXME: if you're using a POST body and query string parameters, using this
72
+ # method will convert those parameters on the query string into parameters in
73
+ # the body. this is broken, and should be fixed.
74
+ def set_oauth_body
75
+ self.set_form_data(@oauth_helper.parameters_with_oauth)
76
+ params_with_sig = @oauth_helper.parameters.merge(:oauth_signature => @oauth_helper.signature)
77
+ self.set_form_data(params_with_sig)
78
+ end
79
+
80
+ def set_oauth_query_string
81
+ oauth_params_str = @oauth_helper.oauth_parameters.map { |k,v| [escape(k), escape(v)] * "=" }.join("&")
82
+
83
+ uri = URI.parse(path)
84
+ if uri.query.to_s == ""
85
+ uri.query = oauth_params_str
86
+ else
87
+ uri.query = uri.query + "&" + oauth_params_str
88
+ end
89
+
90
+ @path = uri.to_s
91
+
92
+ @path << "&oauth_signature=#{escape(oauth_helper.signature)}"
93
+ end
94
+ end
@@ -0,0 +1,297 @@
1
+ require 'net/http'
2
+ require 'net/https'
3
+ require 'oauth/oauth'
4
+ require 'oauth/client/net_http'
5
+ require 'oauth/errors'
6
+
7
+ module OAuth
8
+ class Consumer
9
+ # determine the certificate authority path to verify SSL certs
10
+ CA_FILES = %w(/etc/ssl/certs/ca-certificates.crt /usr/share/curl/curl-ca-bundle.crt)
11
+ CA_FILES.each do |ca_file|
12
+ if File.exists?(ca_file)
13
+ CA_FILE = ca_file
14
+ break
15
+ end
16
+ end
17
+ CA_FILE = nil unless defined?(CA_FILE)
18
+
19
+ @@default_options = {
20
+ # Signature method used by server. Defaults to HMAC-SHA1
21
+ :signature_method => 'HMAC-SHA1',
22
+
23
+ # default paths on site. These are the same as the defaults set up by the generators
24
+ :request_token_path => '/oauth/request_token',
25
+ :authorize_path => '/oauth/authorize',
26
+ :access_token_path => '/oauth/access_token',
27
+
28
+ # How do we send the oauth values to the server see
29
+ # http://oauth.net/core/1.0/#consumer_req_param for more info
30
+ #
31
+ # Possible values:
32
+ #
33
+ # :header - via the Authorize header (Default) ( option 1. in spec)
34
+ # :body - url form encoded in body of POST request ( option 2. in spec)
35
+ # :query_string - via the query part of the url ( option 3. in spec)
36
+ :scheme => :header,
37
+
38
+ # Default http method used for OAuth Token Requests (defaults to :post)
39
+ :http_method => :post,
40
+
41
+ :oauth_version => "1.0"
42
+ }
43
+
44
+ attr_accessor :options, :key, :secret
45
+ attr_writer :site, :http
46
+
47
+ # Create a new consumer instance by passing it a configuration hash:
48
+ #
49
+ # @consumer = OAuth::Consumer.new(key, secret, {
50
+ # :site => "http://term.ie",
51
+ # :scheme => :header,
52
+ # :http_method => :post,
53
+ # :request_token_path => "/oauth/example/request_token.php",
54
+ # :access_token_path => "/oauth/example/access_token.php",
55
+ # :authorize_path => "/oauth/example/authorize.php"
56
+ # })
57
+ #
58
+ # Start the process by requesting a token
59
+ #
60
+ # @request_token = @consumer.get_request_token
61
+ # session[:request_token] = @request_token
62
+ # redirect_to @request_token.authorize_url
63
+ #
64
+ # When user returns create an access_token
65
+ #
66
+ # @access_token = @request_token.get_access_token
67
+ # @photos=@access_token.get('/photos.xml')
68
+ #
69
+ def initialize(consumer_key, consumer_secret, options = {})
70
+ @key = consumer_key
71
+ @secret = consumer_secret
72
+
73
+ # ensure that keys are symbols
74
+ @options = @@default_options.merge(options.inject({}) { |options, (key, value)|
75
+ options[key.to_sym] = value
76
+ options
77
+ })
78
+ end
79
+
80
+ # The default http method
81
+ def http_method
82
+ @http_method ||= @options[:http_method] || :post
83
+ end
84
+
85
+ # The HTTP object for the site. The HTTP Object is what you get when you do Net::HTTP.new
86
+ def http
87
+ @http ||= create_http
88
+ end
89
+
90
+ # Contains the root URI for this site
91
+ def uri(custom_uri = nil)
92
+ if custom_uri
93
+ @uri = custom_uri
94
+ @http = create_http # yike, oh well. less intrusive this way
95
+ else # if no custom passed, we use existing, which, if unset, is set to site uri
96
+ @uri ||= URI.parse(site)
97
+ end
98
+ end
99
+
100
+ # Makes a request to the service for a new OAuth::RequestToken
101
+ #
102
+ # @request_token = @consumer.get_request_token
103
+ #
104
+ def get_request_token(request_options = {}, *arguments)
105
+ 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])
107
+ end
108
+
109
+ # Creates, signs and performs an http request.
110
+ # 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.
112
+ #
113
+ # @consumer.request(:get, '/people', @token, { :scheme => :query_string })
114
+ # @consumer.request(:post, '/people', @token, {}, @person.to_xml, { 'Content-Type' => 'application/xml' })
115
+ #
116
+ def request(http_method, path, token = nil, request_options = {}, *arguments)
117
+ if path !~ /^\//
118
+ @http = create_http(path)
119
+ _uri = URI.parse(path)
120
+ path = "#{_uri.path}#{_uri.query ? "?#{_uri.query}" : ""}"
121
+ end
122
+
123
+ rsp = http.request(create_signed_request(http_method, path, token, request_options, *arguments))
124
+
125
+ # check for an error reported by the Problem Reporting extension
126
+ # (http://wiki.oauth.net/ProblemReporting)
127
+ # note: a 200 may actually be an error; check for an oauth_problem key to be sure
128
+ if !(headers = rsp.to_hash["www-authenticate"]).nil? &&
129
+ (h = headers.select { |h| h =~ /^OAuth / }).any? &&
130
+ h.first =~ /oauth_problem/
131
+
132
+ # puts "Header: #{h.first}"
133
+
134
+ # TODO doesn't handle broken responses from api.login.yahoo.com
135
+ # remove debug code when done
136
+ params = OAuth::Helper.parse_header(h.first)
137
+
138
+ # puts "Params: #{params.inspect}"
139
+ # puts "Body: #{rsp.body}"
140
+
141
+ raise OAuth::Problem.new(params.delete("oauth_problem"), rsp, params)
142
+ end
143
+
144
+ rsp
145
+ end
146
+
147
+ # Creates and signs an http request.
148
+ # It's recommended to use the Token classes to set this up correctly
149
+ def create_signed_request(http_method, path, token = nil, request_options = {}, *arguments)
150
+ request = create_http_request(http_method, path, *arguments)
151
+ sign!(request, token, request_options)
152
+ request
153
+ end
154
+
155
+ # Creates a request and parses the result as url_encoded. This is used internally for the RequestToken and AccessToken requests.
156
+ def token_request(http_method, path, token = nil, request_options = {}, *arguments)
157
+ response = request(http_method, path, token, request_options, *arguments)
158
+
159
+ case response.code.to_i
160
+
161
+ when (200..299)
162
+ CGI.parse(response.body).inject({}) { |h,(k,v)| h[k.to_sym] = v.first; h }
163
+ when (300..399)
164
+ # this is a redirect
165
+ response.error!
166
+ when (400..499)
167
+ raise OAuth::Unauthorized, response
168
+ else
169
+ response.error!
170
+ end
171
+ end
172
+
173
+ # Sign the Request object. Use this if you have an externally generated http request object you want to sign.
174
+ def sign!(request, token = nil, request_options = {})
175
+ request.oauth!(http, self, token, options.merge(request_options))
176
+ end
177
+
178
+ # Return the signature_base_string
179
+ def signature_base_string(request, token = nil, request_options = {})
180
+ request.signature_base_string(http, self, token, options.merge(request_options))
181
+ end
182
+
183
+ def site
184
+ @options[:site].to_s
185
+ end
186
+
187
+ def scheme
188
+ @options[:scheme]
189
+ end
190
+
191
+ def request_token_path
192
+ @options[:request_token_path]
193
+ end
194
+
195
+ def authorize_path
196
+ @options[:authorize_path]
197
+ end
198
+
199
+ def access_token_path
200
+ @options[:access_token_path]
201
+ end
202
+
203
+ # TODO this is ugly, rewrite
204
+ def request_token_url
205
+ @options[:request_token_url] || site + request_token_path
206
+ end
207
+
208
+ def request_token_url?
209
+ @options.has_key?(:request_token_url)
210
+ end
211
+
212
+ def authorize_url
213
+ @options[:authorize_url] || site + authorize_path
214
+ end
215
+
216
+ def authorize_url?
217
+ @options.has_key?(:authorize_url)
218
+ end
219
+
220
+ def access_token_url
221
+ @options[:access_token_url] || site + access_token_path
222
+ end
223
+
224
+ def access_token_url?
225
+ @options.has_key?(:access_token_url)
226
+ end
227
+
228
+ protected
229
+
230
+ # Instantiates the http object
231
+ def create_http(_url = nil)
232
+ if _url.nil? || _url[0] =~ /^\//
233
+ our_uri = URI.parse(site)
234
+ else
235
+ our_uri = URI.parse(_url)
236
+ end
237
+
238
+ http_object = Net::HTTP.new(our_uri.host, our_uri.port)
239
+
240
+ http_object.use_ssl = (our_uri.scheme == 'https')
241
+
242
+ if CA_FILE
243
+ http_object.ca_file = CA_FILE
244
+ http_object.verify_mode = OpenSSL::SSL::VERIFY_PEER
245
+ http_object.verify_depth = 5
246
+ else
247
+ http_object.verify_mode = OpenSSL::SSL::VERIFY_NONE
248
+ end
249
+
250
+ http_object
251
+ end
252
+
253
+ # create the http request object for a given http_method and path
254
+ def create_http_request(http_method, path, *arguments)
255
+ http_method = http_method.to_sym
256
+
257
+ if [:post, :put].include?(http_method)
258
+ data = arguments.shift
259
+ end
260
+
261
+ headers = arguments.first.is_a?(Hash) ? arguments.shift : {}
262
+
263
+ case http_method
264
+ when :post
265
+ request = Net::HTTP::Post.new(path,headers)
266
+ request["Content-Length"] = 0 # Default to 0
267
+ when :put
268
+ request = Net::HTTP::Put.new(path,headers)
269
+ request["Content-Length"] = 0 # Default to 0
270
+ when :get
271
+ request = Net::HTTP::Get.new(path,headers)
272
+ when :delete
273
+ request = Net::HTTP::Delete.new(path,headers)
274
+ when :head
275
+ request = Net::HTTP::Head.new(path,headers)
276
+ else
277
+ raise ArgumentError, "Don't know how to handle http_method: :#{http_method.to_s}"
278
+ end
279
+
280
+ if data.is_a?(Hash)
281
+ request.set_form_data(data)
282
+ elsif data
283
+ request.body = data.to_s
284
+ request["Content-Length"] = request.body.length
285
+ end
286
+
287
+ request
288
+ end
289
+
290
+ # Unset cached http instance because it cannot be marshalled when
291
+ # it has already been used and use_ssl is set to true
292
+ def marshal_dump(*args)
293
+ @http = nil
294
+ self
295
+ end
296
+ end
297
+ end