signet 0.11.0 → 0.12.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +47 -36
- data/Gemfile +5 -4
- data/README.md +4 -5
- data/Rakefile +86 -37
- data/lib/signet.rb +17 -14
- data/lib/signet/errors.rb +4 -4
- data/lib/signet/oauth_1.rb +128 -153
- data/lib/signet/oauth_1/client.rb +309 -343
- data/lib/signet/oauth_1/credential.rb +40 -37
- data/lib/signet/oauth_1/server.rb +197 -203
- data/lib/signet/oauth_1/signature_methods/hmac_sha1.rb +11 -10
- data/lib/signet/oauth_1/signature_methods/plaintext.rb +8 -7
- data/lib/signet/oauth_1/signature_methods/rsa_sha1.rb +11 -11
- data/lib/signet/oauth_2.rb +41 -43
- data/lib/signet/oauth_2/client.rb +302 -313
- data/lib/signet/version.rb +2 -73
- data/signet.gemspec +37 -39
- data/spec/signet/oauth_1/client_spec.rb +313 -315
- data/spec/signet/oauth_1/credential_spec.rb +64 -56
- data/spec/signet/oauth_1/server_spec.rb +362 -362
- data/spec/signet/oauth_1/signature_methods/hmac_sha1_spec.rb +26 -26
- data/spec/signet/oauth_1/signature_methods/plaintext_spec.rb +28 -28
- data/spec/signet/oauth_1/signature_methods/rsa_sha1_spec.rb +34 -35
- data/spec/signet/oauth_1_spec.rb +527 -524
- data/spec/signet/oauth_2/client_spec.rb +612 -576
- data/spec/signet/oauth_2_spec.rb +88 -89
- data/spec/signet_spec.rb +41 -41
- data/spec/spec_helper.rb +7 -7
- data/spec/spec_helper_spec.rb +8 -8
- metadata +50 -43
- data/tasks/clobber.rake +0 -2
- data/tasks/gem.rake +0 -34
- data/tasks/git.rake +0 -40
- data/tasks/metrics.rake +0 -41
- data/tasks/spec.rake +0 -34
- data/tasks/wiki.rake +0 -38
- data/tasks/yard.rake +0 -21
@@ -15,6 +15,11 @@
|
|
15
15
|
module Signet #:nodoc:
|
16
16
|
module OAuth1
|
17
17
|
class Credential
|
18
|
+
# rubocop:disable Metrics/AbcSize
|
19
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
20
|
+
# rubocop:disable Metrics/MethodLength
|
21
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
22
|
+
|
18
23
|
##
|
19
24
|
# Creates a token object from a key and secret.
|
20
25
|
#
|
@@ -34,7 +39,7 @@ module Signet #:nodoc:
|
|
34
39
|
# Signet::OAuth1::Credential.new(
|
35
40
|
# "dpf43f3p2l4k3l03", "kd94hf93k423kf44"
|
36
41
|
# )
|
37
|
-
def initialize
|
42
|
+
def initialize *args
|
38
43
|
# We want to be particularly flexible in how we initialize a token
|
39
44
|
# object for maximum interoperability. However, this flexibility
|
40
45
|
# means we need to be careful about returning an unexpected value for
|
@@ -45,73 +50,71 @@ module Signet #:nodoc:
|
|
45
50
|
# indifferent access. Also uglier.
|
46
51
|
key_from_hash = lambda do |parameters|
|
47
52
|
parameters["oauth_token"] ||
|
48
|
-
|
49
|
-
|
50
|
-
|
53
|
+
parameters[:oauth_token] ||
|
54
|
+
parameters["key"] ||
|
55
|
+
parameters[:key]
|
51
56
|
end
|
52
57
|
secret_from_hash = lambda do |parameters|
|
53
58
|
parameters["oauth_token_secret"] ||
|
54
|
-
|
55
|
-
|
56
|
-
|
59
|
+
parameters[:oauth_token_secret] ||
|
60
|
+
parameters["secret"] ||
|
61
|
+
parameters[:secret]
|
57
62
|
end
|
58
|
-
if args.first.respond_to?
|
63
|
+
if args.first.respond_to? :to_hash
|
59
64
|
parameters = args.first.to_hash
|
60
|
-
@key = key_from_hash.call
|
61
|
-
@secret = secret_from_hash.call
|
65
|
+
@key = key_from_hash.call parameters
|
66
|
+
@secret = secret_from_hash.call parameters
|
62
67
|
unless @key && @secret
|
63
68
|
raise ArgumentError,
|
64
|
-
|
69
|
+
"Could not find both key and secret in #{hash.inspect}."
|
65
70
|
end
|
66
71
|
else
|
67
72
|
# Normalize to an Array
|
68
|
-
if !args.first.
|
69
|
-
|
70
|
-
|
73
|
+
if !args.first.is_a?(String) &&
|
74
|
+
!args.first.respond_to?(:to_str) &&
|
75
|
+
args.first.is_a?(Enumerable)
|
71
76
|
# We need to special-case strings since they're technically
|
72
77
|
# Enumerable objects.
|
73
78
|
args = args.first.to_a
|
74
|
-
elsif args.first.respond_to?
|
79
|
+
elsif args.first.respond_to? :to_ary
|
75
80
|
args = args.first.to_ary
|
76
81
|
end
|
77
|
-
if args.all? { |value| value.
|
78
|
-
parameters = args.
|
79
|
-
@key = key_from_hash.call
|
80
|
-
@secret = secret_from_hash.call
|
82
|
+
if args.all? { |value| value.is_a? Array }
|
83
|
+
parameters = args.each_with_object({}) { |(k, v), h| h[k] = v; }
|
84
|
+
@key = key_from_hash.call parameters
|
85
|
+
@secret = secret_from_hash.call parameters
|
81
86
|
elsif args.size == 2
|
82
87
|
@key, @secret = args
|
83
88
|
else
|
84
89
|
raise ArgumentError,
|
85
|
-
|
90
|
+
"wrong number of arguments (#{args.size} for 2)"
|
86
91
|
end
|
87
92
|
end
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
end
|
93
|
-
if @secret.respond_to?(:to_str)
|
94
|
-
@secret = @secret.to_str
|
95
|
-
else
|
96
|
-
raise TypeError, "Expected String, got #{@secret.class}."
|
97
|
-
end
|
93
|
+
raise TypeError, "Expected String, got #{@key.class}." unless @key.respond_to? :to_str
|
94
|
+
@key = @key.to_str
|
95
|
+
raise TypeError, "Expected String, got #{@secret.class}." unless @secret.respond_to? :to_str
|
96
|
+
@secret = @secret.to_str
|
98
97
|
end
|
98
|
+
# rubocop:enable Metrics/AbcSize
|
99
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
100
|
+
# rubocop:enable Metrics/MethodLength
|
101
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
99
102
|
|
100
103
|
attr_accessor :key, :secret
|
101
104
|
|
102
105
|
def to_hash
|
103
|
-
|
104
|
-
"oauth_token"
|
105
|
-
"oauth_token_secret" =>
|
106
|
+
{
|
107
|
+
"oauth_token" => key,
|
108
|
+
"oauth_token_secret" => secret
|
106
109
|
}
|
107
110
|
end
|
108
|
-
|
111
|
+
alias to_h to_hash
|
109
112
|
|
110
|
-
def ==
|
113
|
+
def == other
|
111
114
|
if other.respond_to?(:key) && other.respond_to?(:secret)
|
112
|
-
|
115
|
+
key == other.key && secret == other.secret
|
113
116
|
else
|
114
|
-
|
117
|
+
false
|
115
118
|
end
|
116
119
|
end
|
117
120
|
end
|
@@ -12,18 +12,17 @@
|
|
12
12
|
# See the License for the specific language governing permissions and
|
13
13
|
# limitations under the License.
|
14
14
|
#
|
15
|
-
require
|
16
|
-
require
|
17
|
-
require
|
18
|
-
require
|
19
|
-
require
|
20
|
-
require
|
21
|
-
require
|
15
|
+
require "faraday"
|
16
|
+
require "stringio"
|
17
|
+
require "addressable/uri"
|
18
|
+
require "signet"
|
19
|
+
require "signet/errors"
|
20
|
+
require "signet/oauth_1"
|
21
|
+
require "signet/oauth_1/credential"
|
22
22
|
|
23
23
|
module Signet
|
24
24
|
module OAuth1
|
25
25
|
class Server
|
26
|
-
|
27
26
|
# @return [Proc] lookup the value from this Proc.
|
28
27
|
attr_accessor :nonce_timestamp, :client_credential, :token_credential,
|
29
28
|
:temporary_credential, :verifier
|
@@ -50,19 +49,21 @@ module Signet
|
|
50
49
|
# :verifier =>
|
51
50
|
# lambda {|verifier| Verifier.find_by_verifier(verifier).active? }
|
52
51
|
# )
|
53
|
-
def initialize
|
52
|
+
def initialize options = {}
|
54
53
|
[:nonce_timestamp, :client_credential, :token_credential,
|
55
54
|
:temporary_credential, :verifier].each do |attr|
|
56
|
-
|
55
|
+
instance_variable_set "@#{attr}", options[attr]
|
57
56
|
end
|
58
57
|
end
|
59
|
-
|
58
|
+
# rubocop:disable Naming/UncommunicativeMethodParamName
|
59
|
+
|
60
60
|
# Constant time string comparison.
|
61
|
-
def safe_equals?
|
61
|
+
def safe_equals? a, b
|
62
62
|
check = a.bytesize ^ b.bytesize
|
63
63
|
a.bytes.zip(b.bytes) { |x, y| check |= x ^ y.to_i }
|
64
|
-
check
|
64
|
+
check.zero?
|
65
65
|
end
|
66
|
+
# rubocop:enable Naming/UncommunicativeMethodParamName
|
66
67
|
|
67
68
|
##
|
68
69
|
# Determine if the supplied nonce/timestamp pair is valid by calling
|
@@ -71,10 +72,11 @@ module Signet
|
|
71
72
|
# @param [String, #to_str] nonce value from the request
|
72
73
|
# @param [String, #to_str] timestamp value from the request
|
73
74
|
# @return [Boolean] if the nonce/timestamp pair is valid.
|
74
|
-
def validate_nonce_timestamp
|
75
|
-
|
76
|
-
|
77
|
-
@nonce_timestamp.
|
75
|
+
def validate_nonce_timestamp nonce, timestamp
|
76
|
+
if @nonce_timestamp.respond_to? :call
|
77
|
+
nonce =
|
78
|
+
@nonce_timestamp.call nonce, timestamp
|
79
|
+
end
|
78
80
|
nonce ? true : false
|
79
81
|
end
|
80
82
|
|
@@ -84,8 +86,8 @@ module Signet
|
|
84
86
|
#
|
85
87
|
# @param [String] key provided to the {#client_credential} Proc.
|
86
88
|
# @return [Signet::OAuth1::Credential] The client credential.
|
87
|
-
def find_client_credential
|
88
|
-
call_credential_lookup
|
89
|
+
def find_client_credential key
|
90
|
+
call_credential_lookup @client_credential, key
|
89
91
|
end
|
90
92
|
|
91
93
|
##
|
@@ -94,8 +96,8 @@ module Signet
|
|
94
96
|
#
|
95
97
|
# @param [String] key provided to the {#token_credential} Proc.
|
96
98
|
# @return [Signet::OAuth1::Credential] if the credential is found.
|
97
|
-
def find_token_credential
|
98
|
-
call_credential_lookup
|
99
|
+
def find_token_credential key
|
100
|
+
call_credential_lookup @token_credential, key
|
99
101
|
end
|
100
102
|
|
101
103
|
##
|
@@ -104,8 +106,8 @@ module Signet
|
|
104
106
|
#
|
105
107
|
# @param [String] key provided to the {#temporary_credential} Proc.
|
106
108
|
# @return [Signet::OAuth1::Credential] if the credential is found.
|
107
|
-
def find_temporary_credential
|
108
|
-
call_credential_lookup
|
109
|
+
def find_temporary_credential key
|
110
|
+
call_credential_lookup @temporary_credential, key
|
109
111
|
end
|
110
112
|
|
111
113
|
##
|
@@ -115,17 +117,17 @@ module Signet
|
|
115
117
|
# @param [String] key provided to the Proc in <code>credential</code>
|
116
118
|
# @return [Signet::OAuth1::Credential] credential provided by
|
117
119
|
# <code>credential</code> (if any).
|
118
|
-
def call_credential_lookup
|
119
|
-
cred = credential.call
|
120
|
-
credential.respond_to?
|
120
|
+
def call_credential_lookup credential, key
|
121
|
+
cred = credential.call key if
|
122
|
+
credential.respond_to? :call
|
121
123
|
return nil if cred.nil?
|
122
|
-
return nil unless
|
123
|
-
|
124
|
-
|
125
|
-
if
|
124
|
+
return nil unless cred.respond_to?(:to_str) ||
|
125
|
+
cred.respond_to?(:to_ary) ||
|
126
|
+
cred.respond_to?(:to_hash)
|
127
|
+
if cred.instance_of? ::Signet::OAuth1::Credential
|
126
128
|
cred
|
127
129
|
else
|
128
|
-
::Signet::OAuth1::Credential.new
|
130
|
+
::Signet::OAuth1::Credential.new cred
|
129
131
|
end
|
130
132
|
end
|
131
133
|
|
@@ -135,11 +137,12 @@ module Signet
|
|
135
137
|
# @param [String] verifier Key provided to the {#verifier} Proc.
|
136
138
|
# @return [Boolean] if the verifier Proc returns anything other than
|
137
139
|
# <code>nil</code> or <code>false</code>.
|
138
|
-
def find_verifier
|
139
|
-
verified = @verifier.call
|
140
|
+
def find_verifier verifier
|
141
|
+
verified = @verifier.call verifier if @verifier.respond_to? :call
|
140
142
|
verified ? true : false
|
141
143
|
end
|
142
|
-
|
144
|
+
# rubocop:disable Metrics/MethodLength
|
145
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
143
146
|
|
144
147
|
##
|
145
148
|
# Validate and normalize the components from an HTTP request.
|
@@ -151,12 +154,12 @@ module Signet
|
|
151
154
|
# @param [StringIO, String] body The HTTP body.
|
152
155
|
# @param [HTTPAdapter] adapter The HTTP adapter(optional).
|
153
156
|
# @return [Hash] normalized request components
|
154
|
-
def verify_request_components
|
157
|
+
def verify_request_components options = {}
|
155
158
|
if options[:request]
|
156
|
-
if options[:request].
|
159
|
+
if options[:request].is_a?(Faraday::Request) || options[:request].is_a?(Array)
|
157
160
|
request = options[:request]
|
158
161
|
elsif options[:adapter]
|
159
|
-
request = options[:adapter].adapt_request
|
162
|
+
request = options[:adapter].adapt_request options[:request]
|
160
163
|
end
|
161
164
|
method = request.method
|
162
165
|
uri = request.path
|
@@ -166,45 +169,43 @@ module Signet
|
|
166
169
|
method = options[:method] || :get
|
167
170
|
uri = options[:uri]
|
168
171
|
headers = options[:headers] || []
|
169
|
-
body = options[:body] ||
|
172
|
+
body = options[:body] || ""
|
170
173
|
end
|
171
174
|
|
172
|
-
headers = headers.to_a if headers.
|
175
|
+
headers = headers.to_a if headers.is_a? Hash
|
173
176
|
method = method.to_s.upcase
|
174
177
|
|
175
178
|
request_components = {
|
176
|
-
:method
|
177
|
-
:uri
|
178
|
-
:
|
179
|
+
method: method,
|
180
|
+
uri: uri,
|
181
|
+
headers: headers
|
179
182
|
}
|
180
183
|
|
181
184
|
# Verify that we have all the pieces required to validate the HTTP request
|
182
185
|
request_components.each do |(key, value)|
|
183
|
-
unless value
|
184
|
-
raise ArgumentError, "Missing :#{key} parameter."
|
185
|
-
end
|
186
|
+
raise ArgumentError, "Missing :#{key} parameter." unless value
|
186
187
|
end
|
187
188
|
request_components[:body] = body
|
188
189
|
request_components
|
189
190
|
end
|
191
|
+
# rubocop:enable Metrics/MethodLength
|
192
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
190
193
|
|
191
194
|
##
|
192
195
|
# Validate and normalize the HTTP Authorization header.
|
193
196
|
#
|
194
197
|
# @param [Array] headers from HTTP request.
|
195
198
|
# @return [Hash] Hash of Authorization header.
|
196
|
-
def verify_auth_header_components
|
197
|
-
auth_header = headers.find{|x| x[0] ==
|
198
|
-
if
|
199
|
-
raise MalformedAuthorizationError.new('Authorization header is missing')
|
200
|
-
end
|
199
|
+
def verify_auth_header_components headers
|
200
|
+
auth_header = headers.find { |x| x[0] == "Authorization" }
|
201
|
+
raise MalformedAuthorizationError, "Authorization header is missing" if auth_header.nil? || auth_header[1] == ""
|
201
202
|
auth_hash = ::Signet::OAuth1.parse_authorization_header(
|
202
|
-
auth_header[1]
|
203
|
+
auth_header[1]
|
204
|
+
).each_with_object({}) { |(key, val), acc| acc[key.downcase] = val; }
|
203
205
|
|
204
206
|
auth_hash
|
205
207
|
end
|
206
208
|
|
207
|
-
|
208
209
|
##
|
209
210
|
# @overload request_realm(options)
|
210
211
|
# @param [Hash] request A pre-constructed request to verify.
|
@@ -214,27 +215,31 @@ module Signet
|
|
214
215
|
# @param [StringIO, String] body The HTTP body.
|
215
216
|
# @param [HTTPAdapter] adapter The HTTP adapter(optional).
|
216
217
|
# @return [String] The Authorization realm(see RFC 2617) of the request.
|
217
|
-
def request_realm
|
218
|
-
if
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
218
|
+
def request_realm options = {}
|
219
|
+
request_components = if options[:request]
|
220
|
+
verify_request_components(
|
221
|
+
request: options[:request],
|
222
|
+
adapter: options[:adapter]
|
223
|
+
)
|
224
|
+
else
|
225
|
+
verify_request_components(
|
226
|
+
method: options[:method],
|
227
|
+
uri: options[:uri],
|
228
|
+
headers: options[:headers],
|
229
|
+
body: options[:body]
|
230
|
+
)
|
231
|
+
end
|
232
|
+
|
233
|
+
auth_header = request_components[:headers].find { |x| x[0] == "Authorization" }
|
234
|
+
raise MalformedAuthorizationError, "Authorization header is missing" if auth_header.nil? || auth_header[1] == ""
|
234
235
|
auth_hash = ::Signet::OAuth1.parse_authorization_header(
|
235
|
-
auth_header[1]
|
236
|
-
|
236
|
+
auth_header[1]
|
237
|
+
).each_with_object({}) { |(key, val), acc| acc[key.downcase] = val; }
|
238
|
+
auth_hash["realm"]
|
237
239
|
end
|
240
|
+
# rubocop:disable Metrics/AbcSize
|
241
|
+
# rubocop:disable Metrics/MethodLength
|
242
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
238
243
|
|
239
244
|
##
|
240
245
|
# Authenticates a temporary credential request. If no oauth_callback is
|
@@ -248,60 +253,62 @@ module Signet
|
|
248
253
|
# @param [StringIO, String] body The HTTP body.
|
249
254
|
# @param [HTTPAdapter] adapter The HTTP adapter(optional).
|
250
255
|
# @return [String] The oauth_callback value, or <code>false</code> if not valid.
|
251
|
-
def authenticate_temporary_credential_request
|
256
|
+
def authenticate_temporary_credential_request options = {}
|
252
257
|
verifications = {
|
253
|
-
:
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
}
|
258
|
+
client_credential: lambda { |_x|
|
259
|
+
::Signet::OAuth1::Credential.new("Client credential key",
|
260
|
+
"Client credential secret")
|
261
|
+
}
|
258
262
|
}
|
259
263
|
verifications.each do |(key, _value)|
|
260
|
-
raise ArgumentError, "#{key} was not set." unless
|
264
|
+
raise ArgumentError, "#{key} was not set." unless send key
|
261
265
|
end
|
262
266
|
|
263
|
-
if
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
267
|
+
request_components = if options[:request]
|
268
|
+
verify_request_components(
|
269
|
+
request: options[:request],
|
270
|
+
adapter: options[:adapter]
|
271
|
+
)
|
272
|
+
else
|
273
|
+
verify_request_components(
|
274
|
+
method: options[:method],
|
275
|
+
uri: options[:uri],
|
276
|
+
headers: options[:headers]
|
277
|
+
)
|
278
|
+
end
|
273
279
|
# body should be blank; we don't care in any case.
|
274
280
|
method = request_components[:method]
|
275
281
|
uri = request_components[:uri]
|
276
282
|
headers = request_components[:headers]
|
277
283
|
|
278
|
-
auth_hash = verify_auth_header_components
|
279
|
-
return false unless(client_credential = find_client_credential(
|
280
|
-
|
284
|
+
auth_hash = verify_auth_header_components headers
|
285
|
+
return false unless (client_credential = find_client_credential(
|
286
|
+
auth_hash["oauth_consumer_key"]
|
287
|
+
))
|
281
288
|
|
282
|
-
return false unless validate_nonce_timestamp(auth_hash[
|
283
|
-
auth_hash[
|
289
|
+
return false unless validate_nonce_timestamp(auth_hash["oauth_nonce"],
|
290
|
+
auth_hash["oauth_timestamp"])
|
284
291
|
client_credential_secret = client_credential.secret if client_credential
|
285
292
|
|
286
293
|
computed_signature = ::Signet::OAuth1.sign_parameters(
|
287
294
|
method,
|
288
295
|
uri,
|
289
296
|
# Realm isn't used, and will throw the signature off.
|
290
|
-
auth_hash.reject{|k,
|
297
|
+
auth_hash.reject { |k, _v| k == "realm" }.to_a,
|
291
298
|
client_credential_secret,
|
292
299
|
nil
|
293
300
|
)
|
294
|
-
if safe_equals?
|
295
|
-
if
|
296
|
-
|
301
|
+
if safe_equals? computed_signature, auth_hash["oauth_signature"]
|
302
|
+
if auth_hash.fetch("oauth_callback", "oob").empty?
|
303
|
+
"oob"
|
297
304
|
else
|
298
|
-
auth_hash.fetch
|
305
|
+
auth_hash.fetch "oauth_callback"
|
299
306
|
end
|
300
307
|
else
|
301
308
|
false
|
302
309
|
end
|
303
310
|
end
|
304
|
-
|
311
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
305
312
|
|
306
313
|
##
|
307
314
|
# Authenticates a token credential request.
|
@@ -314,70 +321,66 @@ module Signet
|
|
314
321
|
# @param [HTTPAdapter] adapter The HTTP adapter(optional).
|
315
322
|
# @return [Hash] A hash of credentials and realm for a valid request,
|
316
323
|
# or <code>nil</code> if not valid.
|
317
|
-
def authenticate_token_credential_request
|
324
|
+
def authenticate_token_credential_request options = {}
|
318
325
|
verifications = {
|
319
|
-
:
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
:
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
:
|
328
|
-
lambda {|x| 'Verifier' }
|
326
|
+
client_credential: lambda { |_x|
|
327
|
+
::Signet::OAuth1::Credential.new("Client credential key",
|
328
|
+
"Client credential secret")
|
329
|
+
},
|
330
|
+
temporary_credential: lambda { |_x|
|
331
|
+
::Signet::OAuth1::Credential.new("Temporary credential key",
|
332
|
+
"Temporary credential secret")
|
333
|
+
},
|
334
|
+
verifier: ->(_x) { "Verifier" }
|
329
335
|
}
|
330
336
|
verifications.each do |(key, _value)|
|
331
|
-
unless
|
332
|
-
raise ArgumentError, "#{key} was not set."
|
333
|
-
end
|
334
|
-
end
|
335
|
-
if(options[:request])
|
336
|
-
request_components = verify_request_components(
|
337
|
-
:request=>options[:request],
|
338
|
-
:adapter=>options[:adapter]
|
339
|
-
)
|
340
|
-
else
|
341
|
-
request_components = verify_request_components(
|
342
|
-
:method=>options[:method],
|
343
|
-
:uri=>options[:uri],
|
344
|
-
:headers=>options[:headers],
|
345
|
-
:body=>options[:body]
|
346
|
-
)
|
337
|
+
raise ArgumentError, "#{key} was not set." unless send key
|
347
338
|
end
|
339
|
+
request_components = if options[:request]
|
340
|
+
verify_request_components(
|
341
|
+
request: options[:request],
|
342
|
+
adapter: options[:adapter]
|
343
|
+
)
|
344
|
+
else
|
345
|
+
verify_request_components(
|
346
|
+
method: options[:method],
|
347
|
+
uri: options[:uri],
|
348
|
+
headers: options[:headers],
|
349
|
+
body: options[:body]
|
350
|
+
)
|
351
|
+
end
|
348
352
|
# body should be blank; we don't care in any case.
|
349
353
|
method = request_components[:method]
|
350
354
|
uri = request_components[:uri]
|
351
355
|
headers = request_components[:headers]
|
352
356
|
|
353
|
-
auth_hash = verify_auth_header_components
|
354
|
-
return false unless(
|
355
|
-
client_credential = find_client_credential
|
357
|
+
auth_hash = verify_auth_header_components headers
|
358
|
+
return false unless (
|
359
|
+
client_credential = find_client_credential auth_hash["oauth_consumer_key"]
|
356
360
|
)
|
357
|
-
return false unless(
|
358
|
-
temporary_credential = find_temporary_credential
|
361
|
+
return false unless (
|
362
|
+
temporary_credential = find_temporary_credential auth_hash["oauth_token"]
|
359
363
|
)
|
360
364
|
return false unless validate_nonce_timestamp(
|
361
|
-
auth_hash[
|
365
|
+
auth_hash["oauth_nonce"], auth_hash["oauth_timestamp"]
|
366
|
+
)
|
362
367
|
|
363
368
|
computed_signature = ::Signet::OAuth1.sign_parameters(
|
364
369
|
method,
|
365
370
|
uri,
|
366
371
|
# Realm isn't used, and will throw the signature off.
|
367
|
-
auth_hash.reject{|k,
|
372
|
+
auth_hash.reject { |k, _v| k == "realm" }.to_a,
|
368
373
|
client_credential.secret,
|
369
374
|
temporary_credential.secret
|
370
375
|
)
|
371
376
|
|
372
|
-
|
373
|
-
|
374
|
-
|
375
|
-
|
376
|
-
}
|
377
|
-
else
|
378
|
-
nil
|
379
|
-
end
|
377
|
+
return nil unless safe_equals? computed_signature, auth_hash["oauth_signature"]
|
378
|
+
{ client_credential: client_credential,
|
379
|
+
temporary_credential: temporary_credential,
|
380
|
+
realm: auth_hash["realm"] }
|
380
381
|
end
|
382
|
+
# rubocop:disable Metrics/CyclomaticComplexity
|
383
|
+
# rubocop:disable Metrics/PerceivedComplexity
|
381
384
|
|
382
385
|
##
|
383
386
|
# Authenticates a request for a protected resource.
|
@@ -392,96 +395,88 @@ module Signet
|
|
392
395
|
#
|
393
396
|
# @return [Hash] A hash of the credentials and realm for a valid request,
|
394
397
|
# or <code>nil</code> if not valid.
|
395
|
-
def authenticate_resource_request
|
398
|
+
def authenticate_resource_request options = {}
|
396
399
|
verifications = {
|
397
|
-
:
|
398
|
-
|
399
|
-
|
400
|
-
|
401
|
-
end
|
400
|
+
client_credential: lambda do |_x|
|
401
|
+
::Signet::OAuth1::Credential.new("Client credential key",
|
402
|
+
"Client credential secret")
|
403
|
+
end
|
402
404
|
}
|
403
405
|
|
404
|
-
unless
|
406
|
+
unless options[:two_legged] == true
|
405
407
|
verifications.update(
|
406
|
-
:
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
end
|
408
|
+
token_credential: lambda do |_x|
|
409
|
+
::Signet::OAuth1::Credential.new("Token credential key",
|
410
|
+
"Token credential secret")
|
411
|
+
end
|
411
412
|
)
|
412
413
|
end
|
413
414
|
# Make sure all required state is set
|
414
415
|
verifications.each do |(key, _value)|
|
415
|
-
unless
|
416
|
-
raise ArgumentError, "#{key} was not set."
|
417
|
-
end
|
416
|
+
raise ArgumentError, "#{key} was not set." unless send key
|
418
417
|
end
|
419
418
|
|
420
|
-
if
|
421
|
-
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
|
430
|
-
|
419
|
+
request_components = if options[:request]
|
420
|
+
verify_request_components(
|
421
|
+
request: options[:request],
|
422
|
+
adapter: options[:adapter]
|
423
|
+
)
|
424
|
+
else
|
425
|
+
verify_request_components(
|
426
|
+
method: options[:method],
|
427
|
+
uri: options[:uri],
|
428
|
+
headers: options[:headers],
|
429
|
+
body: options[:body]
|
430
|
+
)
|
431
|
+
end
|
431
432
|
method = request_components[:method]
|
432
433
|
uri = request_components[:uri]
|
433
434
|
headers = request_components[:headers]
|
434
435
|
body = request_components[:body]
|
435
436
|
|
436
437
|
|
437
|
-
if !body.
|
438
|
+
if !body.is_a?(String) && body.respond_to?(:each)
|
438
439
|
# Just in case we get a chunked body
|
439
440
|
merged_body = StringIO.new
|
440
441
|
body.each do |chunk|
|
441
|
-
merged_body.write
|
442
|
+
merged_body.write chunk
|
442
443
|
end
|
443
444
|
body = merged_body.string
|
444
445
|
end
|
445
|
-
|
446
|
-
raise TypeError, "Expected String, got #{body.class}."
|
447
|
-
end
|
446
|
+
raise TypeError, "Expected String, got #{body.class}." unless body.is_a? String
|
448
447
|
|
449
448
|
media_type = nil
|
450
449
|
headers.each do |(header, value)|
|
451
|
-
if header.
|
452
|
-
media_type = value.gsub(/^([^;]+)(;.*?)?$/, '\1')
|
453
|
-
end
|
450
|
+
media_type = value.gsub(/^([^;]+)(;.*?)?$/, '\1') if header.casecmp("Content-Type").zero?
|
454
451
|
end
|
455
452
|
|
456
|
-
auth_hash = verify_auth_header_components
|
453
|
+
auth_hash = verify_auth_header_components headers
|
457
454
|
|
458
|
-
auth_token = auth_hash[
|
455
|
+
auth_token = auth_hash["oauth_token"]
|
459
456
|
|
460
457
|
|
461
|
-
unless
|
462
|
-
return nil if
|
463
|
-
return nil unless(token_credential = find_token_credential
|
458
|
+
unless options[:two_legged]
|
459
|
+
return nil if auth_token.nil?
|
460
|
+
return nil unless (token_credential = find_token_credential auth_token)
|
464
461
|
token_credential_secret = token_credential.secret if token_credential
|
465
462
|
end
|
466
463
|
|
467
|
-
return nil unless(client_credential =
|
468
|
-
|
464
|
+
return nil unless (client_credential =
|
465
|
+
find_client_credential auth_hash["oauth_consumer_key"])
|
469
466
|
|
470
|
-
return nil unless validate_nonce_timestamp(auth_hash[
|
471
|
-
|
467
|
+
return nil unless validate_nonce_timestamp(auth_hash["oauth_nonce"],
|
468
|
+
auth_hash["oauth_timestamp"])
|
472
469
|
|
473
|
-
if
|
474
|
-
media_type ==
|
470
|
+
if method == ("POST" || "PUT") &&
|
471
|
+
media_type == "application/x-www-form-urlencoded"
|
475
472
|
request_components[:body] = body
|
476
|
-
post_parameters = Addressable::URI.form_unencode
|
477
|
-
post_parameters.each {|param| param[1] = "" if param[1].nil?}
|
473
|
+
post_parameters = Addressable::URI.form_unencode body
|
474
|
+
post_parameters.each { |param| param[1] = "" if param[1].nil? }
|
478
475
|
# If the auth header doesn't have the same params as the body, it
|
479
476
|
# can't have been signed correctly(5849#3.4.1.3)
|
480
|
-
unless
|
481
|
-
raise MalformedAuthorizationError
|
482
|
-
|
483
|
-
'but Authentication header did not include form values'
|
484
|
-
)
|
477
|
+
unless post_parameters.sort == auth_hash.reject { |k, _v| k.index "oauth_" }.to_a.sort
|
478
|
+
raise MalformedAuthorizationError, "Request is of type application/x-www-form-urlencoded " \
|
479
|
+
"but Authentication header did not include form values"
|
485
480
|
end
|
486
481
|
end
|
487
482
|
|
@@ -491,21 +486,20 @@ module Signet
|
|
491
486
|
method,
|
492
487
|
uri,
|
493
488
|
# Realm isn't used, and will throw the signature off.
|
494
|
-
auth_hash.reject{|k,
|
489
|
+
auth_hash.reject { |k, _v| k == "realm" }.to_a,
|
495
490
|
client_credential_secret,
|
496
491
|
token_credential_secret
|
497
492
|
)
|
498
493
|
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
}
|
504
|
-
else
|
505
|
-
nil
|
506
|
-
end
|
494
|
+
return nil unless safe_equals? computed_signature, auth_hash["oauth_signature"]
|
495
|
+
{ client_credential: client_credential,
|
496
|
+
token_credential: token_credential,
|
497
|
+
realm: auth_hash["realm"] }
|
507
498
|
end
|
508
|
-
|
499
|
+
# rubocop:enable Metrics/AbcSize
|
500
|
+
# rubocop:enable Metrics/CyclomaticComplexity
|
501
|
+
# rubocop:enable Metrics/MethodLength
|
502
|
+
# rubocop:enable Metrics/PerceivedComplexity
|
509
503
|
end
|
510
504
|
end
|
511
505
|
end
|