PageRenderTime 67ms CodeModel.GetById 37ms RepoModel.GetById 0ms app.codeStats 1ms

/vendor/gems/mojodna-oauth-0.3.5/lib/oauth/consumer.rb

https://github.com/tomtaylor/iamnear
Ruby | 347 lines | 212 code | 51 blank | 84 comment | 16 complexity | 7d919bffe56412d392db4bfd38eaba35 MD5 | raw file
Possible License(s): GPL-2.0
  1. require 'net/http'
  2. require 'net/https'
  3. require 'oauth/oauth'
  4. require 'oauth/client/net_http'
  5. require 'oauth/errors'
  6. module OAuth
  7. class Consumer
  8. # determine the certificate authority path to verify SSL certs
  9. CA_FILES = %w(/etc/ssl/certs/ca-certificates.crt /usr/share/curl/curl-ca-bundle.crt)
  10. CA_FILES.each do |ca_file|
  11. if File.exists?(ca_file)
  12. CA_FILE = ca_file
  13. break
  14. end
  15. end
  16. CA_FILE = nil unless defined?(CA_FILE)
  17. @@default_options = {
  18. # Signature method used by server. Defaults to HMAC-SHA1
  19. :signature_method => 'HMAC-SHA1',
  20. # default paths on site. These are the same as the defaults set up by the generators
  21. :request_token_path => '/oauth/request_token',
  22. :authorize_path => '/oauth/authorize',
  23. :access_token_path => '/oauth/access_token',
  24. :proxy => nil,
  25. # How do we send the oauth values to the server see
  26. # http://oauth.net/core/1.0/#consumer_req_param for more info
  27. #
  28. # Possible values:
  29. #
  30. # :header - via the Authorize header (Default) ( option 1. in spec)
  31. # :body - url form encoded in body of POST request ( option 2. in spec)
  32. # :query_string - via the query part of the url ( option 3. in spec)
  33. :scheme => :header,
  34. # Default http method used for OAuth Token Requests (defaults to :post)
  35. :http_method => :post,
  36. :oauth_version => "1.0"
  37. }
  38. attr_accessor :options, :key, :secret
  39. attr_writer :site, :http
  40. # Create a new consumer instance by passing it a configuration hash:
  41. #
  42. # @consumer = OAuth::Consumer.new(key, secret, {
  43. # :site => "http://term.ie",
  44. # :scheme => :header,
  45. # :http_method => :post,
  46. # :request_token_path => "/oauth/example/request_token.php",
  47. # :access_token_path => "/oauth/example/access_token.php",
  48. # :authorize_path => "/oauth/example/authorize.php"
  49. # })
  50. #
  51. # Start the process by requesting a token
  52. #
  53. # @request_token = @consumer.get_request_token
  54. # session[:request_token] = @request_token
  55. # redirect_to @request_token.authorize_url
  56. #
  57. # When user returns create an access_token
  58. #
  59. # @access_token = @request_token.get_access_token
  60. # @photos=@access_token.get('/photos.xml')
  61. #
  62. def initialize(consumer_key, consumer_secret, options = {})
  63. @key = consumer_key
  64. @secret = consumer_secret
  65. # ensure that keys are symbols
  66. @options = @@default_options.merge(options.inject({}) { |options, (key, value)|
  67. options[key.to_sym] = value
  68. options
  69. })
  70. end
  71. # The default http method
  72. def http_method
  73. @http_method ||= @options[:http_method] || :post
  74. end
  75. # The HTTP object for the site. The HTTP Object is what you get when you do Net::HTTP.new
  76. def http
  77. @http ||= create_http
  78. end
  79. # Contains the root URI for this site
  80. def uri(custom_uri = nil)
  81. if custom_uri
  82. @uri = custom_uri
  83. @http = create_http # yike, oh well. less intrusive this way
  84. else # if no custom passed, we use existing, which, if unset, is set to site uri
  85. @uri ||= URI.parse(site)
  86. end
  87. end
  88. def get_access_token(request_token, request_options = {}, *arguments)
  89. response = token_request(http_method, (access_token_url? ? access_token_url : access_token_path), request_token, request_options, *arguments)
  90. OAuth::AccessToken.from_hash(self, response)
  91. end
  92. # Makes a request to the service for a new OAuth::RequestToken
  93. #
  94. # @request_token = @consumer.get_request_token
  95. #
  96. # To include OAuth parameters:
  97. #
  98. # @request_token = @consumer.get_request_token \
  99. # :oauth_callback => "http://example.com/cb"
  100. #
  101. # To include application-specific parameters:
  102. #
  103. # @request_token = @consumer.get_request_token({}, :foo => "bar")
  104. #
  105. # TODO oauth_callback should be a mandatory parameter
  106. def get_request_token(request_options = {}, *arguments)
  107. # if oauth_callback wasn't provided, it is assumed that oauth_verifiers
  108. # will be exchanged out of band
  109. request_options[:oauth_callback] ||= OAuth::OUT_OF_BAND
  110. response = token_request(http_method, (request_token_url? ? request_token_url : request_token_path), nil, request_options, *arguments)
  111. OAuth::RequestToken.from_hash(self, response)
  112. end
  113. # Creates, signs and performs an http request.
  114. # It's recommended to use the OAuth::Token classes to set this up correctly.
  115. # request_options take precedence over consumer-wide options when signing
  116. # a request.
  117. # arguments are POST and PUT bodies (a Hash, string-encoded parameters, or
  118. # absent), followed by additional HTTP headers.
  119. #
  120. # @consumer.request(:get, '/people', @token, { :scheme => :query_string })
  121. # @consumer.request(:post, '/people', @token, {}, @person.to_xml, { 'Content-Type' => 'application/xml' })
  122. #
  123. def request(http_method, path, token = nil, request_options = {}, *arguments)
  124. if path !~ /^\//
  125. @http = create_http(path)
  126. _uri = URI.parse(path)
  127. path = "#{_uri.path}#{_uri.query ? "?#{_uri.query}" : ""}"
  128. end
  129. rsp = http.request(create_signed_request(http_method, path, token, request_options, *arguments))
  130. # check for an error reported by the Problem Reporting extension
  131. # (http://wiki.oauth.net/ProblemReporting)
  132. # note: a 200 may actually be an error; check for an oauth_problem key to be sure
  133. if !(headers = rsp.to_hash["www-authenticate"]).nil? &&
  134. (h = headers.select { |h| h =~ /^OAuth / }).any? &&
  135. h.first =~ /oauth_problem/
  136. # puts "Header: #{h.first}"
  137. # TODO doesn't handle broken responses from api.login.yahoo.com
  138. # remove debug code when done
  139. params = OAuth::Helper.parse_header(h.first)
  140. # puts "Params: #{params.inspect}"
  141. # puts "Body: #{rsp.body}"
  142. raise OAuth::Problem.new(params.delete("oauth_problem"), rsp, params)
  143. end
  144. rsp
  145. end
  146. # Creates and signs an http request.
  147. # It's recommended to use the Token classes to set this up correctly
  148. def create_signed_request(http_method, path, token = nil, request_options = {}, *arguments)
  149. request = create_http_request(http_method, path, *arguments)
  150. sign!(request, token, request_options)
  151. request
  152. end
  153. # Creates a request and parses the result as url_encoded. This is used internally for the RequestToken and AccessToken requests.
  154. def token_request(http_method, path, token = nil, request_options = {}, *arguments)
  155. response = request(http_method, path, token, request_options, *arguments)
  156. case response.code.to_i
  157. when (200..299)
  158. # symbolize keys
  159. # TODO this could be considered unexpected behavior; symbols or not?
  160. # TODO this also drops subsequent values from multi-valued keys
  161. CGI.parse(response.body).inject({}) do |h,(k,v)|
  162. h[k.to_sym] = v.first
  163. h[k] = v.first
  164. h
  165. end
  166. when (300..399)
  167. # this is a redirect
  168. response.error!
  169. when (400..499)
  170. raise OAuth::Unauthorized, response
  171. else
  172. response.error!
  173. end
  174. end
  175. # Sign the Request object. Use this if you have an externally generated http request object you want to sign.
  176. def sign!(request, token = nil, request_options = {})
  177. request.oauth!(http, self, token, options.merge(request_options))
  178. end
  179. # Return the signature_base_string
  180. def signature_base_string(request, token = nil, request_options = {})
  181. request.signature_base_string(http, self, token, options.merge(request_options))
  182. end
  183. def site
  184. @options[:site].to_s
  185. end
  186. def scheme
  187. @options[:scheme]
  188. end
  189. def request_token_path
  190. @options[:request_token_path]
  191. end
  192. def authorize_path
  193. @options[:authorize_path]
  194. end
  195. def access_token_path
  196. @options[:access_token_path]
  197. end
  198. # TODO this is ugly, rewrite
  199. def request_token_url
  200. @options[:request_token_url] || site + request_token_path
  201. end
  202. def request_token_url?
  203. @options.has_key?(:request_token_url)
  204. end
  205. def authorize_url
  206. @options[:authorize_url] || site + authorize_path
  207. end
  208. def authorize_url?
  209. @options.has_key?(:authorize_url)
  210. end
  211. def access_token_url
  212. @options[:access_token_url] || site + access_token_path
  213. end
  214. def access_token_url?
  215. @options.has_key?(:access_token_url)
  216. end
  217. def proxy
  218. @options[:proxy]
  219. end
  220. protected
  221. # Instantiates the http object
  222. def create_http(_url = nil)
  223. if _url.nil? || _url[0] =~ /^\//
  224. our_uri = URI.parse(site)
  225. else
  226. our_uri = URI.parse(_url)
  227. end
  228. if proxy.nil?
  229. http_object = Net::HTTP.new(our_uri.host, our_uri.port)
  230. else
  231. proxy_uri = proxy.is_a?(URI) ? proxy : URI.parse(proxy)
  232. http_object = Net::HTTP.new(our_uri.host, our_uri.port, proxy_uri.host, proxy_uri.port, proxy_uri.user, proxy_uri.password)
  233. end
  234. http_object.use_ssl = (our_uri.scheme == 'https')
  235. if CA_FILE
  236. http_object.ca_file = CA_FILE
  237. http_object.verify_mode = OpenSSL::SSL::VERIFY_PEER
  238. http_object.verify_depth = 5
  239. else
  240. http_object.verify_mode = OpenSSL::SSL::VERIFY_NONE
  241. end
  242. http_object
  243. end
  244. # create the http request object for a given http_method and path
  245. def create_http_request(http_method, path, *arguments)
  246. http_method = http_method.to_sym
  247. if [:post, :put].include?(http_method)
  248. data = (arguments.shift || {}).reject { |k,v| v.nil? }
  249. end
  250. headers = arguments.first.is_a?(Hash) ? arguments.shift : {}
  251. case http_method
  252. when :post
  253. request = Net::HTTP::Post.new(path,headers)
  254. request["Content-Length"] = 0 # Default to 0
  255. when :put
  256. request = Net::HTTP::Put.new(path,headers)
  257. request["Content-Length"] = 0 # Default to 0
  258. when :get
  259. request = Net::HTTP::Get.new(path,headers)
  260. when :delete
  261. request = Net::HTTP::Delete.new(path,headers)
  262. when :head
  263. request = Net::HTTP::Head.new(path,headers)
  264. else
  265. raise ArgumentError, "Don't know how to handle http_method: :#{http_method.to_s}"
  266. end
  267. if data.is_a?(Hash)
  268. request.set_form_data(data)
  269. elsif data
  270. if data.respond_to?(:read)
  271. request.body_stream = data
  272. if data.respond_to?(:length)
  273. request["Content-Length"] = data.length
  274. elsif data.respond_to?(:stat) && data.stat.respond_to?(:size)
  275. request["Content-Length"] = data.stat.size
  276. else
  277. raise ArgumentError, "Don't know how to send a body_stream that doesn't respond to .length or .stat.size"
  278. end
  279. else
  280. request.body = data.to_s
  281. request["Content-Length"] = request.body.length
  282. end
  283. end
  284. request
  285. end
  286. # Unset cached http instance because it cannot be marshalled when
  287. # it has already been used and use_ssl is set to true
  288. def marshal_dump(*args)
  289. @http = nil
  290. self
  291. end
  292. end
  293. end