signet 0.11.0 → 0.14.1
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
- data/CHANGELOG.md +51 -15
- data/Gemfile +5 -4
- data/README.md +4 -6
- data/Rakefile +107 -37
- data/lib/signet.rb +17 -14
- data/lib/signet/errors.rb +4 -4
- data/lib/signet/oauth_1.rb +129 -154
- 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 +328 -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 +553 -524
- data/spec/signet/oauth_2/client_spec.rb +652 -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 +64 -52
- 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
|