oauth 0.5.14 → 0.6.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +33 -3
- data/README.md +56 -46
- data/SECURITY.md +0 -2
- data/bin/oauth +8 -4
- data/lib/oauth/cli/authorize_command.rb +58 -54
- data/lib/oauth/cli/base_command.rb +163 -159
- data/lib/oauth/cli/help_command.rb +9 -5
- data/lib/oauth/cli/query_command.rb +26 -17
- data/lib/oauth/cli/sign_command.rb +58 -52
- data/lib/oauth/cli/version_command.rb +8 -4
- data/lib/oauth/cli.rb +2 -0
- data/lib/oauth/client/action_controller_request.rb +4 -1
- data/lib/oauth/client/em_http.rb +3 -1
- data/lib/oauth/client/helper.rb +76 -72
- data/lib/oauth/client/net_http.rb +111 -104
- data/lib/oauth/client.rb +2 -0
- data/lib/oauth/consumer.rb +89 -68
- data/lib/oauth/errors/error.rb +2 -0
- data/lib/oauth/errors/problem.rb +3 -0
- data/lib/oauth/errors/unauthorized.rb +4 -0
- data/lib/oauth/errors.rb +2 -0
- data/lib/oauth/helper.rb +9 -5
- data/lib/oauth/oauth.rb +4 -2
- data/lib/oauth/oauth_test_helper.rb +2 -0
- data/lib/oauth/request_proxy/base.rb +4 -4
- data/lib/oauth/request_proxy/mock_request.rb +1 -1
- data/lib/oauth/request_proxy/net_http.rb +8 -8
- data/lib/oauth/request_proxy/rest_client_request.rb +4 -3
- data/lib/oauth/request_proxy.rb +4 -1
- data/lib/oauth/server.rb +8 -4
- data/lib/oauth/signature/base.rb +73 -65
- data/lib/oauth/signature/hmac/sha1.rb +15 -9
- data/lib/oauth/signature/hmac/sha256.rb +15 -9
- data/lib/oauth/signature/plaintext.rb +18 -20
- data/lib/oauth/signature/rsa/sha1.rb +46 -38
- data/lib/oauth/signature.rb +3 -0
- data/lib/oauth/token.rb +2 -0
- data/lib/oauth/tokens/access_token.rb +2 -0
- data/lib/oauth/tokens/consumer_token.rb +2 -0
- data/lib/oauth/tokens/request_token.rb +5 -2
- data/lib/oauth/tokens/server_token.rb +2 -0
- data/lib/oauth/tokens/token.rb +2 -0
- data/lib/oauth/version.rb +5 -1
- data/lib/oauth.rb +9 -2
- metadata +43 -32
data/lib/oauth/consumer.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "net/http"
|
2
4
|
require "net/https"
|
3
5
|
require "oauth/oauth"
|
@@ -17,7 +19,8 @@ module OAuth
|
|
17
19
|
end
|
18
20
|
|
19
21
|
unless defined?(CA_FILE)
|
20
|
-
CA_FILES = %w[/etc/ssl/certs/ca-certificates.crt /etc/pki/tls/certs/ca-bundle.crt
|
22
|
+
CA_FILES = %w[/etc/ssl/certs/ca-certificates.crt /etc/pki/tls/certs/ca-bundle.crt
|
23
|
+
/usr/share/curl/curl-ca-bundle.crt].freeze
|
21
24
|
CA_FILES.each do |ca_file|
|
22
25
|
if File.exist?(ca_file)
|
23
26
|
CA_FILE = ca_file
|
@@ -27,42 +30,49 @@ module OAuth
|
|
27
30
|
end
|
28
31
|
CA_FILE = nil unless defined?(CA_FILE)
|
29
32
|
|
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
|
-
|
33
|
+
@@default_options = SnakyHash::SymbolKeyed.new(
|
34
|
+
{
|
35
|
+
# Signature method used by server. Defaults to HMAC-SHA1
|
36
|
+
signature_method: "HMAC-SHA1",
|
37
|
+
|
38
|
+
# default paths on site. These are the same as the defaults set up by the generators
|
39
|
+
request_token_path: "/oauth/request_token",
|
40
|
+
authenticate_path: "/oauth/authenticate",
|
41
|
+
authorize_path: "/oauth/authorize",
|
42
|
+
access_token_path: "/oauth/access_token",
|
43
|
+
|
44
|
+
proxy: nil,
|
45
|
+
# How do we send the oauth values to the server see
|
46
|
+
# https://oauth.net/core/1.0/#consumer_req_param for more info
|
47
|
+
#
|
48
|
+
# Possible values:
|
49
|
+
#
|
50
|
+
# :header - via the Authorize header (Default) ( option 1. in spec)
|
51
|
+
# :body - url form encoded in body of POST request ( option 2. in spec)
|
52
|
+
# :query_string - via the query part of the url ( option 3. in spec)
|
53
|
+
scheme: :header,
|
54
|
+
|
55
|
+
# Default http method used for OAuth Token Requests (defaults to :post)
|
56
|
+
http_method: :post,
|
57
|
+
|
58
|
+
# Add a custom ca_file for consumer
|
59
|
+
# :ca_file => '/etc/certs.pem'
|
60
|
+
|
61
|
+
# Possible values:
|
62
|
+
#
|
63
|
+
# nil, false - no debug output
|
64
|
+
# true - uses $stdout
|
65
|
+
# some_value - uses some_value
|
66
|
+
debug_output: nil,
|
67
|
+
|
68
|
+
# Defaults to producing a body_hash as part of the signature but
|
69
|
+
# can be disabled since it's not officially part of the OAuth 1.0
|
70
|
+
# spec. Possible values are true and false
|
71
|
+
body_hash_enabled: true,
|
72
|
+
|
73
|
+
oauth_version: "1.0"
|
74
|
+
}
|
75
|
+
)
|
66
76
|
|
67
77
|
attr_accessor :options, :key, :secret
|
68
78
|
attr_writer :site, :http
|
@@ -75,7 +85,8 @@ module OAuth
|
|
75
85
|
# :http_method => :post,
|
76
86
|
# :request_token_path => "/oauth/example/request_token.php",
|
77
87
|
# :access_token_path => "/oauth/example/access_token.php",
|
78
|
-
# :authorize_path => "/oauth/example/authorize.php"
|
88
|
+
# :authorize_path => "/oauth/example/authorize.php",
|
89
|
+
# :body_hash_enabled => false
|
79
90
|
# })
|
80
91
|
#
|
81
92
|
# Start the process by requesting a token
|
@@ -94,9 +105,8 @@ module OAuth
|
|
94
105
|
@secret = consumer_secret
|
95
106
|
|
96
107
|
# ensure that keys are symbols
|
97
|
-
|
98
|
-
|
99
|
-
end)
|
108
|
+
snaky_options = SnakyHash::SymbolKeyed.new(options)
|
109
|
+
@options = @@default_options.merge(snaky_options)
|
100
110
|
end
|
101
111
|
|
102
112
|
# The default http method
|
@@ -105,15 +115,13 @@ module OAuth
|
|
105
115
|
end
|
106
116
|
|
107
117
|
def debug_output
|
108
|
-
@debug_output ||=
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
end
|
116
|
-
end
|
118
|
+
@debug_output ||= case @options[:debug_output]
|
119
|
+
when nil, false
|
120
|
+
when true
|
121
|
+
$stdout
|
122
|
+
else
|
123
|
+
@options[:debug_output]
|
124
|
+
end
|
117
125
|
end
|
118
126
|
|
119
127
|
# The HTTP object for the site. The HTTP Object is what you get when you do Net::HTTP.new
|
@@ -132,7 +140,8 @@ module OAuth
|
|
132
140
|
end
|
133
141
|
|
134
142
|
def get_access_token(request_token, request_options = {}, *arguments, &block)
|
135
|
-
response = token_request(http_method, (access_token_url? ? access_token_url : access_token_path), request_token,
|
143
|
+
response = token_request(http_method, (access_token_url? ? access_token_url : access_token_path), request_token,
|
144
|
+
request_options, *arguments, &block)
|
136
145
|
OAuth::AccessToken.from_hash(self, response)
|
137
146
|
end
|
138
147
|
|
@@ -153,9 +162,11 @@ module OAuth
|
|
153
162
|
def get_request_token(request_options = {}, *arguments, &block)
|
154
163
|
# if oauth_callback wasn't provided, it is assumed that oauth_verifiers
|
155
164
|
# will be exchanged out of band
|
156
|
-
|
165
|
+
unless request_options[:exclude_callback]
|
166
|
+
request_options[:oauth_callback] ||= OAuth::OUT_OF_BAND
|
167
|
+
end
|
157
168
|
|
158
|
-
response = if
|
169
|
+
response = if block
|
159
170
|
token_request(
|
160
171
|
http_method,
|
161
172
|
(request_token_url? ? request_token_url : request_token_path),
|
@@ -165,7 +176,8 @@ module OAuth
|
|
165
176
|
&block
|
166
177
|
)
|
167
178
|
else
|
168
|
-
token_request(http_method, (request_token_url? ? request_token_url : request_token_path), nil,
|
179
|
+
token_request(http_method, (request_token_url? ? request_token_url : request_token_path), nil,
|
180
|
+
request_options, *arguments)
|
169
181
|
end
|
170
182
|
OAuth::RequestToken.from_hash(self, response)
|
171
183
|
end
|
@@ -181,7 +193,7 @@ module OAuth
|
|
181
193
|
# @consumer.request(:post, '/people', @token, {}, @person.to_xml, { 'Content-Type' => 'application/xml' })
|
182
194
|
#
|
183
195
|
def request(http_method, path, token = nil, request_options = {}, *arguments)
|
184
|
-
|
196
|
+
unless %r{^/}.match?(path)
|
185
197
|
@http = create_http(path)
|
186
198
|
_uri = URI.parse(path)
|
187
199
|
path = "#{_uri.path}#{_uri.query ? "?#{_uri.query}" : ""}"
|
@@ -190,13 +202,14 @@ module OAuth
|
|
190
202
|
# override the request with your own, this is useful for file uploads which Net::HTTP does not do
|
191
203
|
req = create_signed_request(http_method, path, token, request_options, *arguments)
|
192
204
|
return nil if block_given? && (yield(req) == :done)
|
205
|
+
|
193
206
|
rsp = http.request(req)
|
194
207
|
# check for an error reported by the Problem Reporting extension
|
195
208
|
# (https://wiki.oauth.net/ProblemReporting)
|
196
209
|
# note: a 200 may actually be an error; check for an oauth_problem key to be sure
|
197
210
|
if !(headers = rsp.to_hash["www-authenticate"]).nil? &&
|
198
|
-
(h = headers.
|
199
|
-
h.first
|
211
|
+
(h = headers.grep(/^OAuth /)).any? &&
|
212
|
+
h.first.include?("oauth_problem")
|
200
213
|
|
201
214
|
# puts "Header: #{h.first}"
|
202
215
|
|
@@ -276,6 +289,7 @@ module OAuth
|
|
276
289
|
|
277
290
|
def request_endpoint
|
278
291
|
return nil if @options[:request_endpoint].nil?
|
292
|
+
|
279
293
|
@options[:request_endpoint].to_s
|
280
294
|
end
|
281
295
|
|
@@ -301,7 +315,7 @@ module OAuth
|
|
301
315
|
|
302
316
|
# TODO: this is ugly, rewrite
|
303
317
|
def request_token_url
|
304
|
-
@options[:request_token_url] || site + request_token_path
|
318
|
+
@options[:request_token_url] || (site + request_token_path)
|
305
319
|
end
|
306
320
|
|
307
321
|
def request_token_url?
|
@@ -309,7 +323,7 @@ module OAuth
|
|
309
323
|
end
|
310
324
|
|
311
325
|
def authenticate_url
|
312
|
-
@options[:authenticate_url] || site + authenticate_path
|
326
|
+
@options[:authenticate_url] || (site + authenticate_path)
|
313
327
|
end
|
314
328
|
|
315
329
|
def authenticate_url?
|
@@ -317,7 +331,7 @@ module OAuth
|
|
317
331
|
end
|
318
332
|
|
319
333
|
def authorize_url
|
320
|
-
@options[:authorize_url] || site + authorize_path
|
334
|
+
@options[:authorize_url] || (site + authorize_path)
|
321
335
|
end
|
322
336
|
|
323
337
|
def authorize_url?
|
@@ -325,7 +339,7 @@ module OAuth
|
|
325
339
|
end
|
326
340
|
|
327
341
|
def access_token_url
|
328
|
-
@options[:access_token_url] || site + access_token_path
|
342
|
+
@options[:access_token_url] || (site + access_token_path)
|
329
343
|
end
|
330
344
|
|
331
345
|
def access_token_url?
|
@@ -342,7 +356,7 @@ module OAuth
|
|
342
356
|
def create_http(_url = nil)
|
343
357
|
_url = request_endpoint unless request_endpoint.nil?
|
344
358
|
|
345
|
-
our_uri = if _url.nil? || _url[0] =~
|
359
|
+
our_uri = if _url.nil? || _url[0] =~ %r{^/}
|
346
360
|
URI.parse(site)
|
347
361
|
else
|
348
362
|
your_uri = URI.parse(_url)
|
@@ -359,7 +373,8 @@ module OAuth
|
|
359
373
|
http_object = Net::HTTP.new(our_uri.host, our_uri.port)
|
360
374
|
else
|
361
375
|
proxy_uri = proxy.is_a?(URI) ? proxy : URI.parse(proxy)
|
362
|
-
http_object = Net::HTTP.new(our_uri.host, our_uri.port, proxy_uri.host, proxy_uri.port, proxy_uri.user,
|
376
|
+
http_object = Net::HTTP.new(our_uri.host, our_uri.port, proxy_uri.host, proxy_uri.port, proxy_uri.user,
|
377
|
+
proxy_uri.password)
|
363
378
|
end
|
364
379
|
|
365
380
|
http_object.use_ssl = (our_uri.scheme == "https")
|
@@ -374,10 +389,14 @@ module OAuth
|
|
374
389
|
end
|
375
390
|
|
376
391
|
http_object.read_timeout = http_object.open_timeout = @options[:timeout] || 60
|
377
|
-
|
392
|
+
if @options[:open_timeout]
|
393
|
+
http_object.open_timeout = @options[:open_timeout]
|
394
|
+
end
|
378
395
|
http_object.ssl_version = @options[:ssl_version] if @options[:ssl_version]
|
379
|
-
|
380
|
-
|
396
|
+
if @options[:ssl_client_cert]
|
397
|
+
http_object.cert = @options[:ssl_client_cert]
|
398
|
+
end
|
399
|
+
http_object.key = @options[:ssl_client_key] if @options[:ssl_client_key]
|
381
400
|
http_object.set_debug_output(debug_output) if debug_output
|
382
401
|
|
383
402
|
http_object
|
@@ -392,8 +411,10 @@ module OAuth
|
|
392
411
|
# if the base site contains a path, add it now
|
393
412
|
# only add if the site host matches the current http object's host
|
394
413
|
# (in case we've specified a full url for token requests)
|
395
|
-
uri
|
396
|
-
|
414
|
+
uri = URI.parse(site)
|
415
|
+
if uri.path && uri.path != "/" && uri.host == http.address
|
416
|
+
path = uri.path + path
|
417
|
+
end
|
397
418
|
|
398
419
|
headers = arguments.first.is_a?(Hash) ? arguments.shift : {}
|
399
420
|
|
data/lib/oauth/errors/error.rb
CHANGED
data/lib/oauth/errors/problem.rb
CHANGED
data/lib/oauth/errors.rb
CHANGED
data/lib/oauth/helper.rb
CHANGED
@@ -1,9 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "time"
|
1
4
|
require "openssl"
|
2
5
|
require "base64"
|
3
6
|
|
4
7
|
module OAuth
|
5
8
|
module Helper
|
6
|
-
|
9
|
+
module_function
|
7
10
|
|
8
11
|
# Escape +value+ by URL encoding all non-reserved character.
|
9
12
|
#
|
@@ -30,7 +33,7 @@ module OAuth
|
|
30
33
|
|
31
34
|
alias generate_nonce generate_key
|
32
35
|
|
33
|
-
def generate_timestamp
|
36
|
+
def generate_timestamp # :nodoc:
|
34
37
|
Time.now.to_i.to_s
|
35
38
|
end
|
36
39
|
|
@@ -43,7 +46,8 @@ module OAuth
|
|
43
46
|
# See Also: {OAuth core spec version 1.0, section 9.1.1}[http://oauth.net/core/1.0#rfc.section.9.1.1]
|
44
47
|
def normalize(params)
|
45
48
|
params.sort.map do |k, values|
|
46
|
-
|
49
|
+
case values
|
50
|
+
when Array
|
47
51
|
# make sure the array has an element so we don't lose the key
|
48
52
|
values << nil if values.empty?
|
49
53
|
# multiple values were provided for a single key
|
@@ -54,7 +58,7 @@ module OAuth
|
|
54
58
|
[escape(k), escape(v)].join("=")
|
55
59
|
end
|
56
60
|
end
|
57
|
-
|
61
|
+
when Hash
|
58
62
|
normalize_nested_query(values, k)
|
59
63
|
else
|
60
64
|
[escape(k), escape(values)].join("=")
|
@@ -99,7 +103,7 @@ module OAuth
|
|
99
103
|
# strip and unescape
|
100
104
|
val = unescape(v.strip)
|
101
105
|
# strip quotes
|
102
|
-
val.sub(
|
106
|
+
val.sub(/^"(.*)"$/, '\1')
|
103
107
|
end
|
104
108
|
|
105
109
|
# convert into a Hash
|
data/lib/oauth/oauth.rb
CHANGED
@@ -1,7 +1,9 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module OAuth
|
2
4
|
# request tokens are passed between the consumer and the provider out of
|
3
5
|
# band (i.e. callbacks cannot be used), per section 6.1.1
|
4
|
-
OUT_OF_BAND = "oob"
|
6
|
+
OUT_OF_BAND = "oob"
|
5
7
|
|
6
8
|
# required parameters, per sections 6.1.1, 6.3.1, and 7
|
7
9
|
PARAMETERS = %w[oauth_callback oauth_consumer_key oauth_token
|
@@ -9,5 +11,5 @@ module OAuth
|
|
9
11
|
oauth_version oauth_signature oauth_body_hash].freeze
|
10
12
|
|
11
13
|
# reserved character regexp, per section 5.1
|
12
|
-
RESERVED_CHARACTERS = /[^a-zA-Z0-9
|
14
|
+
RESERVED_CHARACTERS = /[^a-zA-Z0-9\-._~]/.freeze
|
13
15
|
end
|
@@ -79,15 +79,15 @@ module OAuth
|
|
79
79
|
end
|
80
80
|
|
81
81
|
def parameters_for_signature
|
82
|
-
parameters.
|
82
|
+
parameters.select { |k, _v| !signature_and_unsigned_parameters.include?(k) }
|
83
83
|
end
|
84
84
|
|
85
85
|
def oauth_parameters
|
86
|
-
parameters.select { |k,
|
86
|
+
parameters.select { |k, v| OAuth::PARAMETERS.include?(k) && !v.nil? && v != "" }
|
87
87
|
end
|
88
88
|
|
89
89
|
def non_oauth_parameters
|
90
|
-
parameters.
|
90
|
+
parameters.select { |k, _v| !OAuth::PARAMETERS.include?(k) }
|
91
91
|
end
|
92
92
|
|
93
93
|
def signature_and_unsigned_parameters
|
@@ -127,7 +127,7 @@ module OAuth
|
|
127
127
|
end
|
128
128
|
|
129
129
|
# URI, including OAuth parameters
|
130
|
-
def signed_uri(with_oauth
|
130
|
+
def signed_uri(with_oauth: true)
|
131
131
|
if signed?
|
132
132
|
params = if with_oauth
|
133
133
|
parameters
|
@@ -38,13 +38,11 @@ module OAuth
|
|
38
38
|
request_params = CGI.parse(query_string)
|
39
39
|
# request_params.each{|k,v| request_params[k] = [nil] if v == []}
|
40
40
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
request_params[k] = [v]
|
47
|
-
end
|
41
|
+
options[:parameters]&.each do |k, v|
|
42
|
+
if request_params.key?(k) && v
|
43
|
+
request_params[k] << v
|
44
|
+
else
|
45
|
+
request_params[k] = [v]
|
48
46
|
end
|
49
47
|
end
|
50
48
|
request_params
|
@@ -71,7 +69,9 @@ module OAuth
|
|
71
69
|
end
|
72
70
|
|
73
71
|
def auth_header_params
|
74
|
-
|
72
|
+
unless request["Authorization"] && request["Authorization"][0, 5] == "OAuth"
|
73
|
+
return nil
|
74
|
+
end
|
75
75
|
|
76
76
|
request["Authorization"]
|
77
77
|
end
|
@@ -38,7 +38,8 @@ module OAuth
|
|
38
38
|
|
39
39
|
def post_parameters
|
40
40
|
# Post params are only used if posting form data
|
41
|
-
|
41
|
+
is_form_data = request.payload && request.payload.headers["Content-Type"] == "application/x-www-form-urlencoded"
|
42
|
+
if is_form_data && (method == "POST" || method == "PUT")
|
42
43
|
OAuth::Helper.stringify_keys(query_string_to_hash(request.payload.to_s) || {})
|
43
44
|
else
|
44
45
|
{}
|
@@ -51,9 +52,9 @@ module OAuth
|
|
51
52
|
query.split("&").inject({}) do |result, q|
|
52
53
|
k, v = q.split("=")
|
53
54
|
if !v.nil?
|
54
|
-
result.merge(k => v)
|
55
|
+
result.merge({ k => v })
|
55
56
|
elsif !result.key?(k)
|
56
|
-
result.merge(k => true)
|
57
|
+
result.merge({ k => true })
|
57
58
|
else
|
58
59
|
result
|
59
60
|
end
|
data/lib/oauth/request_proxy.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module OAuth
|
2
4
|
module RequestProxy
|
3
|
-
def self.available_proxies
|
5
|
+
def self.available_proxies # :nodoc:
|
4
6
|
@available_proxies ||= {}
|
5
7
|
end
|
6
8
|
|
@@ -16,6 +18,7 @@ module OAuth
|
|
16
18
|
end
|
17
19
|
|
18
20
|
raise UnknownRequestType, request.class.to_s unless klass
|
21
|
+
|
19
22
|
klass.new(request, options)
|
20
23
|
end
|
21
24
|
|
data/lib/oauth/server.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require "oauth/helper"
|
2
4
|
require "oauth/consumer"
|
3
5
|
|
@@ -31,10 +33,12 @@ module OAuth
|
|
31
33
|
def create_consumer
|
32
34
|
creds = generate_credentials
|
33
35
|
Consumer.new(creds[0], creds[1],
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
36
|
+
{
|
37
|
+
site: base_url,
|
38
|
+
request_token_path: request_token_path,
|
39
|
+
authorize_path: authorize_path,
|
40
|
+
access_token_path: access_token_path
|
41
|
+
})
|
38
42
|
end
|
39
43
|
|
40
44
|
def request_token_path
|