oauth 0.5.6 → 1.1.7
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.
- checksums.yaml +4 -4
- checksums.yaml.gz.sig +0 -0
- data/CHANGELOG.md +915 -0
- data/CITATION.cff +20 -0
- data/CODE_OF_CONDUCT.md +134 -0
- data/CONTRIBUTING.md +272 -0
- data/FUNDING.md +74 -0
- data/LICENSE.md +71 -0
- data/README.md +718 -0
- data/RUBOCOP.md +71 -0
- data/SECURITY.md +21 -0
- data/certs/pboling.pem +27 -0
- data/lib/oauth/auth_sanitizer.rb +36 -0
- data/lib/oauth/client/action_controller_request.rb +33 -22
- data/lib/oauth/client/em_http.rb +110 -103
- data/lib/oauth/client/helper.rb +87 -82
- data/lib/oauth/client/net_http.rb +140 -107
- data/lib/oauth/client.rb +2 -0
- data/lib/oauth/consumer.rb +282 -149
- data/lib/oauth/errors/error.rb +2 -0
- data/lib/oauth/errors/problem.rb +4 -1
- data/lib/oauth/errors/unauthorized.rb +7 -1
- data/lib/oauth/errors.rb +5 -3
- data/lib/oauth/helper.rb +48 -18
- data/lib/oauth/oauth.rb +31 -7
- data/lib/oauth/oauth_test_helper.rb +6 -4
- data/lib/oauth/optional.rb +20 -0
- data/lib/oauth/request_proxy/action_controller_request.rb +53 -71
- data/lib/oauth/request_proxy/action_dispatch_request.rb +42 -4
- data/lib/oauth/request_proxy/base.rb +146 -131
- data/lib/oauth/request_proxy/curb_request.rb +49 -43
- data/lib/oauth/request_proxy/em_http_request.rb +60 -49
- data/lib/oauth/request_proxy/jabber_request.rb +19 -9
- data/lib/oauth/request_proxy/mock_request.rb +5 -3
- data/lib/oauth/request_proxy/net_http.rb +61 -54
- data/lib/oauth/request_proxy/rack_request.rb +31 -31
- data/lib/oauth/request_proxy/rest_client_request.rb +55 -50
- data/lib/oauth/request_proxy/typhoeus_request.rb +51 -45
- data/lib/oauth/request_proxy.rb +21 -14
- data/lib/oauth/server.rb +18 -12
- data/lib/oauth/signature/base.rb +88 -71
- data/lib/oauth/signature/hmac/sha1.rb +16 -10
- data/lib/oauth/signature/hmac/sha256.rb +16 -10
- data/lib/oauth/signature/plaintext.rb +18 -20
- data/lib/oauth/signature/rsa/sha1.rb +71 -38
- data/lib/oauth/signature.rb +41 -34
- data/lib/oauth/token.rb +7 -5
- data/lib/oauth/tokens/access_token.rb +6 -4
- data/lib/oauth/tokens/consumer_token.rb +11 -7
- data/lib/oauth/tokens/request_token.rb +17 -10
- data/lib/oauth/tokens/server_token.rb +2 -1
- data/lib/oauth/tokens/token.rb +15 -1
- data/lib/oauth/version.rb +6 -1
- data/lib/oauth.rb +19 -9
- data/sig/oauth/consumer.rbs +9 -0
- data/sig/oauth/signature/base.rbs +12 -0
- data/sig/oauth/tokens/token.rbs +8 -0
- data/sig/oauth/version.rbs +6 -0
- data.tar.gz.sig +2 -0
- metadata +313 -84
- metadata.gz.sig +3 -0
- data/LICENSE +0 -20
- data/README.rdoc +0 -88
- data/TODO +0 -32
- data/bin/oauth +0 -11
- data/lib/oauth/cli/authorize_command.rb +0 -71
- data/lib/oauth/cli/base_command.rb +0 -208
- data/lib/oauth/cli/help_command.rb +0 -22
- data/lib/oauth/cli/query_command.rb +0 -25
- data/lib/oauth/cli/sign_command.rb +0 -81
- data/lib/oauth/cli/version_command.rb +0 -7
- data/lib/oauth/cli.rb +0 -56
data/lib/oauth/consumer.rb
CHANGED
|
@@ -1,23 +1,44 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
require
|
|
4
|
-
require
|
|
5
|
-
require
|
|
6
|
-
require
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "net/http"
|
|
4
|
+
require "net/https"
|
|
5
|
+
require "oauth/oauth"
|
|
6
|
+
require "oauth/client/net_http"
|
|
7
|
+
require "oauth/errors"
|
|
8
|
+
require "cgi"
|
|
7
9
|
|
|
8
10
|
module OAuth
|
|
11
|
+
# Consumer credentials and request configuration for OAuth 1.0 / 1.0a flows.
|
|
12
|
+
#
|
|
13
|
+
# Includes {OAuth::AUTH_SANITIZER::FilteredAttributes} so inspect output redacts the
|
|
14
|
+
# consumer secret while leaving non-sensitive configuration visible.
|
|
9
15
|
class Consumer
|
|
16
|
+
include OAuth::AUTH_SANITIZER::FilteredAttributes
|
|
17
|
+
|
|
18
|
+
# Instance attributes exposed by the consumer.
|
|
19
|
+
#
|
|
20
|
+
# @!attribute [rw] options
|
|
21
|
+
# @return [Hash] Consumer configuration options
|
|
22
|
+
# @!attribute [rw] key
|
|
23
|
+
# @return [String] OAuth consumer key
|
|
24
|
+
# @!attribute [rw] secret
|
|
25
|
+
# @return [String] OAuth consumer secret (redacted in `#inspect`)
|
|
26
|
+
|
|
10
27
|
# determine the certificate authority path to verify SSL certs
|
|
11
|
-
if ENV[
|
|
12
|
-
if File.exist?(ENV[
|
|
13
|
-
CA_FILE = ENV[
|
|
28
|
+
if ENV["SSL_CERT_FILE"]
|
|
29
|
+
if File.exist?(ENV["SSL_CERT_FILE"])
|
|
30
|
+
CA_FILE = ENV["SSL_CERT_FILE"]
|
|
14
31
|
else
|
|
15
32
|
raise "The SSL CERT provided does not exist."
|
|
16
33
|
end
|
|
17
34
|
end
|
|
18
35
|
|
|
19
|
-
|
|
20
|
-
CA_FILES = %
|
|
36
|
+
unless defined?(CA_FILE)
|
|
37
|
+
CA_FILES = %w[
|
|
38
|
+
/etc/ssl/certs/ca-certificates.crt
|
|
39
|
+
/etc/pki/tls/certs/ca-bundle.crt
|
|
40
|
+
/usr/share/curl/curl-ca-bundle.crt
|
|
41
|
+
].freeze
|
|
21
42
|
CA_FILES.each do |ca_file|
|
|
22
43
|
if File.exist?(ca_file)
|
|
23
44
|
CA_FILE = ca_file
|
|
@@ -27,45 +48,59 @@ module OAuth
|
|
|
27
48
|
end
|
|
28
49
|
CA_FILE = nil unless defined?(CA_FILE)
|
|
29
50
|
|
|
30
|
-
@@default_options =
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
51
|
+
@@default_options = SnakyHash::SymbolKeyed.new(
|
|
52
|
+
{
|
|
53
|
+
# Signature method used by server. Defaults to HMAC-SHA1
|
|
54
|
+
signature_method: "HMAC-SHA1",
|
|
55
|
+
|
|
56
|
+
# default paths on site. These are the same as the defaults set up by the generators
|
|
57
|
+
request_token_path: "/oauth/request_token",
|
|
58
|
+
authenticate_path: "/oauth/authenticate",
|
|
59
|
+
authorize_path: "/oauth/authorize",
|
|
60
|
+
access_token_path: "/oauth/access_token",
|
|
61
|
+
|
|
62
|
+
proxy: nil,
|
|
63
|
+
# How do we send the oauth values to the server see
|
|
64
|
+
# https://oauth.net/core/1.0/#consumer_req_param for more info
|
|
65
|
+
#
|
|
66
|
+
# Possible values:
|
|
67
|
+
#
|
|
68
|
+
# :header - via the Authorize header (Default) ( option 1. in spec)
|
|
69
|
+
# :body - url form encoded in body of POST request ( option 2. in spec)
|
|
70
|
+
# :query_string - via the query part of the url ( option 3. in spec)
|
|
71
|
+
scheme: :header,
|
|
72
|
+
|
|
73
|
+
# Default http method used for OAuth Token Requests (defaults to :post)
|
|
74
|
+
http_method: :post,
|
|
75
|
+
|
|
76
|
+
# Add a custom ca_file for consumer
|
|
77
|
+
# :ca_file => '/etc/certs.pem'
|
|
78
|
+
|
|
79
|
+
# Possible values:
|
|
80
|
+
#
|
|
81
|
+
# nil, false - no debug output
|
|
82
|
+
# true - uses $stdout
|
|
83
|
+
# some_value - uses some_value
|
|
84
|
+
debug_output: nil,
|
|
85
|
+
|
|
86
|
+
# Defaults to producing a body_hash as part of the signature but
|
|
87
|
+
# can be disabled since it's not officially part of the OAuth 1.0
|
|
88
|
+
# spec. Possible values are true and false
|
|
89
|
+
body_hash_enabled: true,
|
|
90
|
+
|
|
91
|
+
oauth_version: "1.0",
|
|
92
|
+
|
|
93
|
+
# Token endpoint redirects are followed only within the same origin by
|
|
94
|
+
# default. Cross-origin redirects can re-sign token requests for an
|
|
95
|
+
# attacker-controlled endpoint, so they require explicit opt-in.
|
|
96
|
+
token_request_max_redirects: 10,
|
|
97
|
+
token_request_cross_origin_redirects: false,
|
|
98
|
+
},
|
|
99
|
+
)
|
|
66
100
|
|
|
67
101
|
attr_accessor :options, :key, :secret
|
|
68
|
-
|
|
102
|
+
filtered_attributes :secret
|
|
103
|
+
attr_writer :site, :http
|
|
69
104
|
|
|
70
105
|
# Create a new consumer instance by passing it a configuration hash:
|
|
71
106
|
#
|
|
@@ -75,7 +110,8 @@ module OAuth
|
|
|
75
110
|
# :http_method => :post,
|
|
76
111
|
# :request_token_path => "/oauth/example/request_token.php",
|
|
77
112
|
# :access_token_path => "/oauth/example/access_token.php",
|
|
78
|
-
# :authorize_path => "/oauth/example/authorize.php"
|
|
113
|
+
# :authorize_path => "/oauth/example/authorize.php",
|
|
114
|
+
# :body_hash_enabled => false
|
|
79
115
|
# })
|
|
80
116
|
#
|
|
81
117
|
# Start the process by requesting a token
|
|
@@ -90,14 +126,12 @@ module OAuth
|
|
|
90
126
|
# @photos=@access_token.get('/photos.xml')
|
|
91
127
|
#
|
|
92
128
|
def initialize(consumer_key, consumer_secret, options = {})
|
|
93
|
-
@key
|
|
129
|
+
@key = consumer_key
|
|
94
130
|
@secret = consumer_secret
|
|
95
131
|
|
|
96
132
|
# ensure that keys are symbols
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
opts
|
|
100
|
-
end)
|
|
133
|
+
snaky_options = SnakyHash::SymbolKeyed.new(options)
|
|
134
|
+
@options = @@default_options.merge(snaky_options)
|
|
101
135
|
end
|
|
102
136
|
|
|
103
137
|
# The default http method
|
|
@@ -106,14 +140,12 @@ module OAuth
|
|
|
106
140
|
end
|
|
107
141
|
|
|
108
142
|
def debug_output
|
|
109
|
-
@debug_output ||=
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
@options[:debug_output]
|
|
116
|
-
end
|
|
143
|
+
@debug_output ||= case @options[:debug_output]
|
|
144
|
+
when nil, false
|
|
145
|
+
when true
|
|
146
|
+
$stdout
|
|
147
|
+
else
|
|
148
|
+
@options[:debug_output]
|
|
117
149
|
end
|
|
118
150
|
end
|
|
119
151
|
|
|
@@ -125,45 +157,92 @@ module OAuth
|
|
|
125
157
|
# Contains the root URI for this site
|
|
126
158
|
def uri(custom_uri = nil)
|
|
127
159
|
if custom_uri
|
|
128
|
-
@uri
|
|
160
|
+
@uri = custom_uri
|
|
129
161
|
@http = create_http # yike, oh well. less intrusive this way
|
|
130
|
-
else
|
|
162
|
+
else # if no custom passed, we use existing, which, if unset, is set to site uri
|
|
131
163
|
@uri ||= URI.parse(site)
|
|
132
164
|
end
|
|
133
165
|
end
|
|
134
166
|
|
|
167
|
+
# Exchanges a verified Request Token for an Access Token.
|
|
168
|
+
#
|
|
169
|
+
# OAuth 1.0 vs 1.0a:
|
|
170
|
+
# - 1.0a requires including oauth_verifier (as returned by the Provider after
|
|
171
|
+
# user authorization) when performing this exchange in a 3‑legged flow.
|
|
172
|
+
# - 1.0 flows did not include oauth_verifier.
|
|
173
|
+
#
|
|
174
|
+
# Usage (3‑legged):
|
|
175
|
+
# access_token = request_token.get_access_token(oauth_verifier: params[:oauth_verifier])
|
|
176
|
+
#
|
|
177
|
+
# @param request_token [OAuth::RequestToken] The authorized request token
|
|
178
|
+
# @param request_options [Hash] OAuth or request options (include :oauth_verifier for 1.0a)
|
|
179
|
+
# @param arguments [Array] Optional POST body and headers
|
|
180
|
+
# @yield [response_body] If a block is given, yields the raw response body.
|
|
181
|
+
# @return [OAuth::AccessToken]
|
|
135
182
|
def get_access_token(request_token, request_options = {}, *arguments, &block)
|
|
136
|
-
response = token_request(
|
|
183
|
+
response = token_request(
|
|
184
|
+
http_method,
|
|
185
|
+
(access_token_url? ? access_token_url : access_token_path),
|
|
186
|
+
request_token,
|
|
187
|
+
request_options,
|
|
188
|
+
*arguments,
|
|
189
|
+
&block
|
|
190
|
+
)
|
|
137
191
|
OAuth::AccessToken.from_hash(self, response)
|
|
138
192
|
end
|
|
139
193
|
|
|
140
194
|
# Makes a request to the service for a new OAuth::RequestToken
|
|
141
195
|
#
|
|
142
|
-
#
|
|
196
|
+
# Example:
|
|
197
|
+
# @request_token = @consumer.get_request_token
|
|
143
198
|
#
|
|
144
199
|
# To include OAuth parameters:
|
|
145
|
-
#
|
|
146
|
-
#
|
|
147
|
-
#
|
|
200
|
+
# @request_token = @consumer.get_request_token(
|
|
201
|
+
# oauth_callback: "http://example.com/cb"
|
|
202
|
+
# )
|
|
148
203
|
#
|
|
149
204
|
# To include application-specific parameters:
|
|
205
|
+
# @request_token = @consumer.get_request_token({}, foo: "bar")
|
|
150
206
|
#
|
|
151
|
-
#
|
|
207
|
+
# OAuth 1.0 vs 1.0a:
|
|
208
|
+
# - In 1.0a, the Consumer SHOULD send oauth_callback when obtaining a request token
|
|
209
|
+
# (or explicitly use OUT_OF_BAND) and the Provider MUST include
|
|
210
|
+
# oauth_callback_confirmed=true in the response.
|
|
211
|
+
# - This library defaults oauth_callback to OUT_OF_BAND ("oob") when not provided,
|
|
212
|
+
# which works for both 1.0 and 1.0a, and mirrors common provider behavior.
|
|
213
|
+
# - The oauth_callback_confirmed response is parsed by the token classes; it is not
|
|
214
|
+
# part of the signature base string and thus is not signed.
|
|
152
215
|
#
|
|
153
|
-
# TODO oauth_callback
|
|
216
|
+
# TODO: In a future major release, oauth_callback may be made mandatory unless
|
|
217
|
+
# request_options[:exclude_callback] is set, to reflect 1.0a guidance.
|
|
218
|
+
#
|
|
219
|
+
# @param request_options [Hash] OAuth options for the request. Notably
|
|
220
|
+
# :oauth_callback can be set to a URL, or OAuth::OUT_OF_BAND ("oob").
|
|
221
|
+
# @param arguments [Array] Optional POST body and headers
|
|
222
|
+
# @yield [response_body] If a block is given, yields the raw response body.
|
|
223
|
+
# @return [OAuth::RequestToken]
|
|
154
224
|
def get_request_token(request_options = {}, *arguments, &block)
|
|
155
225
|
# if oauth_callback wasn't provided, it is assumed that oauth_verifiers
|
|
156
226
|
# will be exchanged out of band
|
|
157
227
|
request_options[:oauth_callback] ||= OAuth::OUT_OF_BAND unless request_options[:exclude_callback]
|
|
158
228
|
|
|
159
|
-
if
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
229
|
+
response = if block
|
|
230
|
+
token_request(
|
|
231
|
+
http_method,
|
|
232
|
+
(request_token_url? ? request_token_url : request_token_path),
|
|
233
|
+
nil,
|
|
234
|
+
request_options,
|
|
235
|
+
*arguments,
|
|
236
|
+
&block
|
|
237
|
+
)
|
|
165
238
|
else
|
|
166
|
-
|
|
239
|
+
token_request(
|
|
240
|
+
http_method,
|
|
241
|
+
(request_token_url? ? request_token_url : request_token_path),
|
|
242
|
+
nil,
|
|
243
|
+
request_options,
|
|
244
|
+
*arguments,
|
|
245
|
+
)
|
|
167
246
|
end
|
|
168
247
|
OAuth::RequestToken.from_hash(self, response)
|
|
169
248
|
end
|
|
@@ -179,26 +258,27 @@ module OAuth
|
|
|
179
258
|
# @consumer.request(:post, '/people', @token, {}, @person.to_xml, { 'Content-Type' => 'application/xml' })
|
|
180
259
|
#
|
|
181
260
|
def request(http_method, path, token = nil, request_options = {}, *arguments)
|
|
182
|
-
|
|
261
|
+
unless %r{^/} =~ path
|
|
183
262
|
@http = create_http(path)
|
|
184
|
-
|
|
185
|
-
path = "#{
|
|
263
|
+
uri = URI.parse(path)
|
|
264
|
+
path = "#{uri.path}#{"?#{uri.query}" if uri.query}"
|
|
186
265
|
end
|
|
187
266
|
|
|
188
267
|
# override the request with your own, this is useful for file uploads which Net::HTTP does not do
|
|
189
268
|
req = create_signed_request(http_method, path, token, request_options, *arguments)
|
|
190
|
-
return
|
|
269
|
+
return if block_given? && (yield(req) == :done)
|
|
270
|
+
|
|
191
271
|
rsp = http.request(req)
|
|
192
272
|
# check for an error reported by the Problem Reporting extension
|
|
193
|
-
# (
|
|
273
|
+
# (https://wiki.oauth.net/ProblemReporting)
|
|
194
274
|
# note: a 200 may actually be an error; check for an oauth_problem key to be sure
|
|
195
275
|
if !(headers = rsp.to_hash["www-authenticate"]).nil? &&
|
|
196
|
-
|
|
197
|
-
|
|
276
|
+
(h = headers.grep(/^OAuth /)).any? &&
|
|
277
|
+
h.first.include?("oauth_problem")
|
|
198
278
|
|
|
199
279
|
# puts "Header: #{h.first}"
|
|
200
280
|
|
|
201
|
-
# TODO doesn't handle broken responses from api.login.yahoo.com
|
|
281
|
+
# TODO: doesn't handle broken responses from api.login.yahoo.com
|
|
202
282
|
# remove debug code when done
|
|
203
283
|
params = OAuth::Helper.parse_header(h.first)
|
|
204
284
|
|
|
@@ -220,36 +300,34 @@ module OAuth
|
|
|
220
300
|
end
|
|
221
301
|
|
|
222
302
|
# Creates a request and parses the result as url_encoded. This is used internally for the RequestToken and AccessToken requests.
|
|
223
|
-
def token_request(http_method, path, token = nil, request_options = {}, *arguments)
|
|
224
|
-
request_options
|
|
225
|
-
response = request(http_method, path, token, request_options, *arguments)
|
|
303
|
+
def token_request(http_method, path, token = nil, request_options = {}, *arguments, &block)
|
|
304
|
+
response = request(http_method, path, token, token_request_options(request_options), *arguments)
|
|
226
305
|
case response.code.to_i
|
|
227
306
|
|
|
228
307
|
when (200..299)
|
|
229
|
-
if
|
|
230
|
-
|
|
308
|
+
if block
|
|
309
|
+
block.call(response.body)
|
|
231
310
|
else
|
|
232
311
|
# symbolize keys
|
|
233
312
|
# TODO this could be considered unexpected behavior; symbols or not?
|
|
234
313
|
# TODO this also drops subsequent values from multi-valued keys
|
|
235
|
-
CGI.parse(response.body).
|
|
314
|
+
CGI.parse(response.body).each_with_object({}) do |(k, v), h|
|
|
236
315
|
h[k.strip.to_sym] = v.first
|
|
237
|
-
h[k.strip]
|
|
238
|
-
h
|
|
316
|
+
h[k.strip] = v.first
|
|
239
317
|
end
|
|
240
318
|
end
|
|
241
319
|
when (300..399)
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
320
|
+
current_uri = token_request_uri(path)
|
|
321
|
+
redirected_uri = token_request_redirect_uri(current_uri, response)
|
|
322
|
+
response.error! unless redirected_uri
|
|
245
323
|
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
324
|
+
redirect_count = request_options[:token_request_redirect_count].to_i + 1
|
|
325
|
+
response.error! if redirect_count > token_request_max_redirects(request_options)
|
|
326
|
+
response.error! if token_request_cross_origin?(current_uri, redirected_uri) &&
|
|
327
|
+
!token_request_cross_origin_redirects?(request_options)
|
|
250
328
|
|
|
251
|
-
|
|
252
|
-
|
|
329
|
+
redirect_options = request_options.merge(token_request_redirect_count: redirect_count)
|
|
330
|
+
token_request(http_method, token_request_redirect_path(current_uri, redirected_uri), token, redirect_options, *arguments, &block)
|
|
253
331
|
when (400..499)
|
|
254
332
|
raise OAuth::Unauthorized, response
|
|
255
333
|
else
|
|
@@ -257,6 +335,55 @@ module OAuth
|
|
|
257
335
|
end
|
|
258
336
|
end
|
|
259
337
|
|
|
338
|
+
def token_request_options(request_options)
|
|
339
|
+
request_options.merge(token_request: true).tap do |options|
|
|
340
|
+
options.delete(:token_request_redirect_count)
|
|
341
|
+
options.delete(:token_request_max_redirects)
|
|
342
|
+
options.delete(:token_request_cross_origin_redirects)
|
|
343
|
+
end
|
|
344
|
+
end
|
|
345
|
+
|
|
346
|
+
def token_request_uri(path)
|
|
347
|
+
uri = URI.parse(path)
|
|
348
|
+
return uri if uri.absolute?
|
|
349
|
+
|
|
350
|
+
URI.parse(site).merge(path)
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
def token_request_redirect_uri(current_uri, response)
|
|
354
|
+
location = response["location"]
|
|
355
|
+
return if location.nil? || location.to_s.empty?
|
|
356
|
+
|
|
357
|
+
current_uri.merge(location)
|
|
358
|
+
end
|
|
359
|
+
|
|
360
|
+
def token_request_redirect_path(current_uri, redirected_uri)
|
|
361
|
+
return redirected_uri.to_s if token_request_cross_origin?(current_uri, redirected_uri)
|
|
362
|
+
|
|
363
|
+
redirected_uri.request_uri
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
def token_request_max_redirects(request_options)
|
|
367
|
+
request_options[:token_request_max_redirects] || options[:token_request_max_redirects]
|
|
368
|
+
end
|
|
369
|
+
|
|
370
|
+
def token_request_cross_origin_redirects?(request_options)
|
|
371
|
+
request_options.fetch(:token_request_cross_origin_redirects, options[:token_request_cross_origin_redirects])
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
def token_request_cross_origin?(current_uri, redirected_uri)
|
|
375
|
+
current_uri.scheme.to_s.downcase != redirected_uri.scheme.to_s.downcase ||
|
|
376
|
+
current_uri.host.to_s.downcase != redirected_uri.host.to_s.downcase ||
|
|
377
|
+
token_request_effective_port(current_uri) != token_request_effective_port(redirected_uri)
|
|
378
|
+
end
|
|
379
|
+
|
|
380
|
+
def token_request_effective_port(uri)
|
|
381
|
+
return uri.port if uri.port
|
|
382
|
+
return 443 if uri.scheme == "https"
|
|
383
|
+
|
|
384
|
+
80 if uri.scheme == "http"
|
|
385
|
+
end
|
|
386
|
+
|
|
260
387
|
# Sign the Request object. Use this if you have an externally generated http request object you want to sign.
|
|
261
388
|
def sign!(request, token = nil, request_options = {})
|
|
262
389
|
request.oauth!(http, self, token, options.merge(request_options))
|
|
@@ -272,7 +399,8 @@ module OAuth
|
|
|
272
399
|
end
|
|
273
400
|
|
|
274
401
|
def request_endpoint
|
|
275
|
-
return
|
|
402
|
+
return if @options[:request_endpoint].nil?
|
|
403
|
+
|
|
276
404
|
@options[:request_endpoint].to_s
|
|
277
405
|
end
|
|
278
406
|
|
|
@@ -296,37 +424,37 @@ module OAuth
|
|
|
296
424
|
@options[:access_token_path]
|
|
297
425
|
end
|
|
298
426
|
|
|
299
|
-
# TODO this is ugly, rewrite
|
|
427
|
+
# TODO: this is ugly, rewrite
|
|
300
428
|
def request_token_url
|
|
301
|
-
@options[:request_token_url] || site + request_token_path
|
|
429
|
+
@options[:request_token_url] || (site + request_token_path)
|
|
302
430
|
end
|
|
303
431
|
|
|
304
432
|
def request_token_url?
|
|
305
|
-
@options.
|
|
433
|
+
@options.key?(:request_token_url)
|
|
306
434
|
end
|
|
307
435
|
|
|
308
436
|
def authenticate_url
|
|
309
|
-
@options[:authenticate_url] || site + authenticate_path
|
|
437
|
+
@options[:authenticate_url] || (site + authenticate_path)
|
|
310
438
|
end
|
|
311
439
|
|
|
312
440
|
def authenticate_url?
|
|
313
|
-
@options.
|
|
441
|
+
@options.key?(:authenticate_url)
|
|
314
442
|
end
|
|
315
443
|
|
|
316
444
|
def authorize_url
|
|
317
|
-
@options[:authorize_url] || site + authorize_path
|
|
445
|
+
@options[:authorize_url] || (site + authorize_path)
|
|
318
446
|
end
|
|
319
447
|
|
|
320
448
|
def authorize_url?
|
|
321
|
-
@options.
|
|
449
|
+
@options.key?(:authorize_url)
|
|
322
450
|
end
|
|
323
451
|
|
|
324
452
|
def access_token_url
|
|
325
|
-
@options[:access_token_url] || site + access_token_path
|
|
453
|
+
@options[:access_token_url] || (site + access_token_path)
|
|
326
454
|
end
|
|
327
455
|
|
|
328
456
|
def access_token_url?
|
|
329
|
-
@options.
|
|
457
|
+
@options.key?(:access_token_url)
|
|
330
458
|
end
|
|
331
459
|
|
|
332
460
|
def proxy
|
|
@@ -336,37 +464,43 @@ module OAuth
|
|
|
336
464
|
protected
|
|
337
465
|
|
|
338
466
|
# Instantiates the http object
|
|
339
|
-
def create_http(
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
if !request_endpoint.nil?
|
|
343
|
-
_url = request_endpoint
|
|
344
|
-
end
|
|
345
|
-
|
|
467
|
+
def create_http(url = nil)
|
|
468
|
+
url = request_endpoint unless request_endpoint.nil?
|
|
346
469
|
|
|
347
|
-
if
|
|
348
|
-
|
|
470
|
+
our_uri = if url.nil? || url[0] =~ %r{^/}
|
|
471
|
+
URI.parse(site)
|
|
349
472
|
else
|
|
350
|
-
|
|
473
|
+
your_uri = URI.parse(url)
|
|
474
|
+
if your_uri.host.nil?
|
|
475
|
+
# If the _url is a path, missing the leading slash, then it won't have a host,
|
|
476
|
+
# and our_uri *must* have a host, so we parse site instead.
|
|
477
|
+
URI.parse(site)
|
|
478
|
+
else
|
|
479
|
+
your_uri
|
|
480
|
+
end
|
|
351
481
|
end
|
|
352
482
|
|
|
353
|
-
|
|
354
483
|
if proxy.nil?
|
|
355
484
|
http_object = Net::HTTP.new(our_uri.host, our_uri.port)
|
|
356
485
|
else
|
|
357
486
|
proxy_uri = proxy.is_a?(URI) ? proxy : URI.parse(proxy)
|
|
358
|
-
http_object = Net::HTTP.new(
|
|
487
|
+
http_object = Net::HTTP.new(
|
|
488
|
+
our_uri.host,
|
|
489
|
+
our_uri.port,
|
|
490
|
+
proxy_uri.host,
|
|
491
|
+
proxy_uri.port,
|
|
492
|
+
proxy_uri.user,
|
|
493
|
+
proxy_uri.password,
|
|
494
|
+
)
|
|
359
495
|
end
|
|
360
496
|
|
|
361
|
-
http_object.use_ssl = (our_uri.scheme ==
|
|
497
|
+
http_object.use_ssl = (our_uri.scheme == "https")
|
|
362
498
|
|
|
363
499
|
if @options[:no_verify]
|
|
364
500
|
http_object.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
|
365
501
|
else
|
|
366
|
-
ca_file =
|
|
367
|
-
if ca_file
|
|
368
|
-
http_object.ca_file = ca_file
|
|
369
|
-
end
|
|
502
|
+
ca_file = @options[:ca_file] || CA_FILE
|
|
503
|
+
http_object.ca_file = ca_file if ca_file
|
|
370
504
|
http_object.verify_mode = OpenSSL::SSL::VERIFY_PEER
|
|
371
505
|
http_object.verify_depth = 5
|
|
372
506
|
end
|
|
@@ -374,6 +508,8 @@ module OAuth
|
|
|
374
508
|
http_object.read_timeout = http_object.open_timeout = @options[:timeout] || 60
|
|
375
509
|
http_object.open_timeout = @options[:open_timeout] if @options[:open_timeout]
|
|
376
510
|
http_object.ssl_version = @options[:ssl_version] if @options[:ssl_version]
|
|
511
|
+
http_object.cert = @options[:ssl_client_cert] if @options[:ssl_client_cert]
|
|
512
|
+
http_object.key = @options[:ssl_client_key] if @options[:ssl_client_key]
|
|
377
513
|
http_object.set_debug_output(debug_output) if debug_output
|
|
378
514
|
|
|
379
515
|
http_object
|
|
@@ -383,41 +519,39 @@ module OAuth
|
|
|
383
519
|
def create_http_request(http_method, path, *arguments)
|
|
384
520
|
http_method = http_method.to_sym
|
|
385
521
|
|
|
386
|
-
if [
|
|
387
|
-
data = arguments.shift
|
|
388
|
-
end
|
|
522
|
+
data = arguments.shift if %i[post put patch].include?(http_method)
|
|
389
523
|
|
|
390
524
|
# if the base site contains a path, add it now
|
|
391
525
|
# only add if the site host matches the current http object's host
|
|
392
526
|
# (in case we've specified a full url for token requests)
|
|
393
|
-
uri
|
|
394
|
-
path = uri.path + path if uri.path && uri.path !=
|
|
527
|
+
uri = URI.parse(site)
|
|
528
|
+
path = uri.path + path if uri.path && uri.path != "/" && uri.host == http.address
|
|
395
529
|
|
|
396
530
|
headers = arguments.first.is_a?(Hash) ? arguments.shift : {}
|
|
397
531
|
|
|
398
532
|
case http_method
|
|
399
533
|
when :post
|
|
400
|
-
request = Net::HTTP::Post.new(path,headers)
|
|
401
|
-
request["Content-Length"] =
|
|
534
|
+
request = Net::HTTP::Post.new(path, headers)
|
|
535
|
+
request["Content-Length"] = "0" # Default to 0
|
|
402
536
|
when :put
|
|
403
|
-
request = Net::HTTP::Put.new(path,headers)
|
|
404
|
-
request["Content-Length"] =
|
|
537
|
+
request = Net::HTTP::Put.new(path, headers)
|
|
538
|
+
request["Content-Length"] = "0" # Default to 0
|
|
405
539
|
when :patch
|
|
406
|
-
request = Net::HTTP::Patch.new(path,headers)
|
|
407
|
-
request["Content-Length"] =
|
|
540
|
+
request = Net::HTTP::Patch.new(path, headers)
|
|
541
|
+
request["Content-Length"] = "0" # Default to 0
|
|
408
542
|
when :get
|
|
409
|
-
request = Net::HTTP::Get.new(path,headers)
|
|
543
|
+
request = Net::HTTP::Get.new(path, headers)
|
|
410
544
|
when :delete
|
|
411
|
-
request =
|
|
545
|
+
request = Net::HTTP::Delete.new(path, headers)
|
|
412
546
|
when :head
|
|
413
|
-
request = Net::HTTP::Head.new(path,headers)
|
|
547
|
+
request = Net::HTTP::Head.new(path, headers)
|
|
414
548
|
else
|
|
415
|
-
raise ArgumentError, "Don't know how to handle http_method: :#{http_method
|
|
549
|
+
raise ArgumentError, "Don't know how to handle http_method: :#{http_method}"
|
|
416
550
|
end
|
|
417
551
|
|
|
418
552
|
if data.is_a?(Hash)
|
|
419
553
|
request.body = OAuth::Helper.normalize(data)
|
|
420
|
-
request.content_type =
|
|
554
|
+
request.content_type = "application/x-www-form-urlencoded"
|
|
421
555
|
elsif data
|
|
422
556
|
if data.respond_to?(:read)
|
|
423
557
|
request.body_stream = data
|
|
@@ -437,13 +571,12 @@ module OAuth
|
|
|
437
571
|
request
|
|
438
572
|
end
|
|
439
573
|
|
|
440
|
-
def marshal_dump(*
|
|
441
|
-
{:
|
|
574
|
+
def marshal_dump(*_args)
|
|
575
|
+
{key: @key, secret: @secret, options: @options}
|
|
442
576
|
end
|
|
443
577
|
|
|
444
578
|
def marshal_load(data)
|
|
445
579
|
initialize(data[:key], data[:secret], data[:options])
|
|
446
580
|
end
|
|
447
|
-
|
|
448
581
|
end
|
|
449
582
|
end
|
data/lib/oauth/errors/error.rb
CHANGED
data/lib/oauth/errors/problem.rb
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module OAuth
|
|
2
4
|
class Problem < OAuth::Unauthorized
|
|
3
5
|
attr_reader :problem, :params
|
|
6
|
+
|
|
4
7
|
def initialize(problem, request = nil, params = {})
|
|
5
8
|
super(request)
|
|
6
9
|
@problem = problem
|
|
7
|
-
@params
|
|
10
|
+
@params = params
|
|
8
11
|
end
|
|
9
12
|
|
|
10
13
|
def to_s
|