signet 0.1.0
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.
- data/CHANGELOG +3 -0
- data/LICENSE +202 -0
- data/README +47 -0
- data/Rakefile +59 -0
- data/lib/compat/digest/hmac.rb +104 -0
- data/lib/compat/securerandom.rb +202 -0
- data/lib/signet.rb +18 -0
- data/lib/signet/errors.rb +38 -0
- data/lib/signet/oauth_1.rb +456 -0
- data/lib/signet/oauth_1/client.rb +1012 -0
- data/lib/signet/oauth_1/credential.rb +119 -0
- data/lib/signet/oauth_1/signature_methods/hmac_sha1.rb +31 -0
- data/lib/signet/version.rb +26 -0
- data/spec/force_compat/digest/hmac.rb +1 -0
- data/spec/force_compat/securerandom.rb +1 -0
- data/spec/signet/oauth_1/client_spec.rb +687 -0
- data/spec/signet/oauth_1/credential_spec.rb +163 -0
- data/spec/signet/oauth_1/services/google_spec.rb +234 -0
- data/spec/signet/oauth_1/signature_methods/hmac_sha1_spec.rb +62 -0
- data/spec/signet/oauth_1_spec.rb +883 -0
- data/spec/spec.opts +2 -0
- data/spec/spec_helper.rb +10 -0
- data/tasks/clobber.rake +2 -0
- data/tasks/gem.rake +71 -0
- data/tasks/git.rake +40 -0
- data/tasks/metrics.rake +41 -0
- data/tasks/rdoc.rake +26 -0
- data/tasks/rubyforge.rake +100 -0
- data/tasks/spec.rake +69 -0
- data/tasks/yard.rake +26 -0
- data/website/index.html +95 -0
- metadata +195 -0
@@ -0,0 +1,1012 @@
|
|
1
|
+
# Copyright (C) 2010 Google Inc.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
|
15
|
+
require 'addressable/uri'
|
16
|
+
require 'signet/oauth_1'
|
17
|
+
require 'signet/oauth_1/credential'
|
18
|
+
require 'signet/errors'
|
19
|
+
|
20
|
+
module Signet #:nodoc:
|
21
|
+
module OAuth1
|
22
|
+
class Client
|
23
|
+
##
|
24
|
+
# Creates an OAuth 1.0 client.
|
25
|
+
#
|
26
|
+
# @param [Hash] options
|
27
|
+
# The configuration parameters for the client.
|
28
|
+
# - <code>:temporary_credential_uri</code> —
|
29
|
+
# The OAuth temporary credentials URI.
|
30
|
+
# - <code>:authorization_uri</code> — The OAuth authorization URI.
|
31
|
+
# - <code>:token_credential_uri</code> —
|
32
|
+
# The OAuth token credentials URI.
|
33
|
+
# - <code>:client_credential_key</code> —
|
34
|
+
# The OAuth client credential key.
|
35
|
+
# - <code>:client_credential_secret</code> —
|
36
|
+
# The OAuth client credential secret.
|
37
|
+
# - <code>:callback</code> — The OAuth callback. Defaults to 'oob'.
|
38
|
+
#
|
39
|
+
# @example
|
40
|
+
# client = Signet::OAuth1::Client.new(
|
41
|
+
# :temporary_credential_uri =>
|
42
|
+
# 'https://www.google.com/accounts/OAuthGetRequestToken',
|
43
|
+
# :authorization_uri =>
|
44
|
+
# 'https://www.google.com/accounts/OAuthAuthorizeToken',
|
45
|
+
# :token_credential_uri =>
|
46
|
+
# 'https://www.google.com/accounts/OAuthGetAccessToken',
|
47
|
+
# :client_credential_key => 'anonymous',
|
48
|
+
# :client_credential_secret => 'anonymous'
|
49
|
+
# )
|
50
|
+
def initialize(options={})
|
51
|
+
self.temporary_credential_uri = options[:temporary_credential_uri]
|
52
|
+
self.authorization_uri = options[:authorization_uri]
|
53
|
+
self.token_credential_uri = options[:token_credential_uri]
|
54
|
+
# Technically... this would allow you to pass in a :client key...
|
55
|
+
# But that would be weird. Don't do that.
|
56
|
+
self.client_credential_key =
|
57
|
+
Signet::OAuth1.extract_credential_key_option(:client, options)
|
58
|
+
self.client_credential_secret =
|
59
|
+
Signet::OAuth1.extract_credential_secret_option(:client, options)
|
60
|
+
self.temporary_credential_key =
|
61
|
+
Signet::OAuth1.extract_credential_key_option(:temporary, options)
|
62
|
+
self.temporary_credential_secret =
|
63
|
+
Signet::OAuth1.extract_credential_secret_option(:temporary, options)
|
64
|
+
self.token_credential_key =
|
65
|
+
Signet::OAuth1.extract_credential_key_option(:token, options)
|
66
|
+
self.token_credential_secret =
|
67
|
+
Signet::OAuth1.extract_credential_secret_option(:token, options)
|
68
|
+
self.callback = options[:callback]
|
69
|
+
end
|
70
|
+
|
71
|
+
##
|
72
|
+
# Returns the temporary credentials URI for this client.
|
73
|
+
#
|
74
|
+
# @return [Addressable::URI] The temporary credentials URI.
|
75
|
+
def temporary_credential_uri
|
76
|
+
return @temporary_credential_uri
|
77
|
+
end
|
78
|
+
alias_method :request_token_uri, :temporary_credential_uri
|
79
|
+
|
80
|
+
##
|
81
|
+
# Sets the temporary credentials URI for this client.
|
82
|
+
#
|
83
|
+
# @param [Addressable::URI, String, #to_str]
|
84
|
+
# new_temporary_credential_uri
|
85
|
+
# The temporary credentials URI.
|
86
|
+
def temporary_credential_uri=(new_temporary_credential_uri)
|
87
|
+
if new_temporary_credential_uri != nil
|
88
|
+
new_temporary_credential_uri =
|
89
|
+
Addressable::URI.parse(new_temporary_credential_uri)
|
90
|
+
@temporary_credential_uri = new_temporary_credential_uri
|
91
|
+
else
|
92
|
+
@temporary_credential_uri = nil
|
93
|
+
end
|
94
|
+
end
|
95
|
+
alias_method :request_token_uri=, :temporary_credential_uri=
|
96
|
+
|
97
|
+
##
|
98
|
+
# Returns the authorization URI that the user should be redirected to.
|
99
|
+
#
|
100
|
+
# @return [Addressable::URI] The authorization URI.
|
101
|
+
#
|
102
|
+
# @see Signet::OAuth1.generate_authorization_uri
|
103
|
+
def authorization_uri(options={})
|
104
|
+
options = options.merge(
|
105
|
+
:temporary_credential_key => self.temporary_credential_key,
|
106
|
+
:callback => self.callback
|
107
|
+
)
|
108
|
+
return nil if @authorization_uri == nil
|
109
|
+
return Addressable::URI.parse(
|
110
|
+
::Signet::OAuth1.generate_authorization_uri(
|
111
|
+
@authorization_uri, options
|
112
|
+
)
|
113
|
+
)
|
114
|
+
end
|
115
|
+
|
116
|
+
##
|
117
|
+
# Sets the authorization URI for this client.
|
118
|
+
#
|
119
|
+
# @param [Addressable::URI, String, #to_str] new_authorization_uri
|
120
|
+
# The authorization URI.
|
121
|
+
def authorization_uri=(new_authorization_uri)
|
122
|
+
if new_authorization_uri != nil
|
123
|
+
new_authorization_uri =
|
124
|
+
Addressable::URI.parse(new_authorization_uri)
|
125
|
+
@authorization_uri = new_authorization_uri
|
126
|
+
else
|
127
|
+
@authorization_uri = nil
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
##
|
132
|
+
# Returns the token credential URI for this client.
|
133
|
+
#
|
134
|
+
# @return [Addressable::URI] The token credential URI.
|
135
|
+
def token_credential_uri
|
136
|
+
return @token_credential_uri
|
137
|
+
end
|
138
|
+
alias_method :access_token_uri, :token_credential_uri
|
139
|
+
|
140
|
+
##
|
141
|
+
# Sets the token credential URI for this client.
|
142
|
+
#
|
143
|
+
# @param [Addressable::URI, String, #to_str] new_token_credential_uri
|
144
|
+
# The token credential URI.
|
145
|
+
def token_credential_uri=(new_token_credential_uri)
|
146
|
+
if new_token_credential_uri != nil
|
147
|
+
new_token_credential_uri =
|
148
|
+
Addressable::URI.parse(new_token_credential_uri)
|
149
|
+
@token_credential_uri = new_token_credential_uri
|
150
|
+
else
|
151
|
+
@token_credential_uri = nil
|
152
|
+
end
|
153
|
+
end
|
154
|
+
alias_method :access_token_uri=, :token_credential_uri=
|
155
|
+
|
156
|
+
# Lots of duplicated code here, but for the sake of auto-generating
|
157
|
+
# documentation, we're going to let it slide. Oh well.
|
158
|
+
|
159
|
+
##
|
160
|
+
# Returns the client credential for this client.
|
161
|
+
#
|
162
|
+
# @return [Signet::OAuth1::Credential] The client credentials.
|
163
|
+
def client_credential
|
164
|
+
if self.client_credential_key && self.client_credential_secret
|
165
|
+
return ::Signet::OAuth1::Credential.new(
|
166
|
+
self.client_credential_key,
|
167
|
+
self.client_credential_secret
|
168
|
+
)
|
169
|
+
elsif !self.client_credential_key && !self.client_credential_secret
|
170
|
+
return nil
|
171
|
+
else
|
172
|
+
raise ArgumentError,
|
173
|
+
"The client credential key and secret must be set."
|
174
|
+
end
|
175
|
+
end
|
176
|
+
alias_method :consumer_token, :client_credential
|
177
|
+
|
178
|
+
##
|
179
|
+
# Sets the client credential for this client.
|
180
|
+
#
|
181
|
+
# @param [Signet::OAuth1::Credential] new_client_credential
|
182
|
+
# The client credentials.
|
183
|
+
def client_credential=(new_client_credential)
|
184
|
+
if new_client_credential != nil
|
185
|
+
if !new_client_credential.kind_of?(::Signet::OAuth1::Credential)
|
186
|
+
raise TypeError,
|
187
|
+
"Expected Signet::OAuth1::Credential, " +
|
188
|
+
"got #{new_client_credential.class}."
|
189
|
+
end
|
190
|
+
@client_credential_key = new_client_credential.key
|
191
|
+
@client_credential_secret = new_client_credential.secret
|
192
|
+
else
|
193
|
+
@client_credential_key = nil
|
194
|
+
@client_credential_secret = nil
|
195
|
+
end
|
196
|
+
end
|
197
|
+
alias_method :consumer_token=, :client_credential=
|
198
|
+
|
199
|
+
##
|
200
|
+
# Returns the client credential key for this client.
|
201
|
+
#
|
202
|
+
# @return [String] The client credential key.
|
203
|
+
def client_credential_key
|
204
|
+
return @client_credential_key
|
205
|
+
end
|
206
|
+
alias_method :consumer_key, :client_credential_key
|
207
|
+
|
208
|
+
##
|
209
|
+
# Sets the client credential key for this client.
|
210
|
+
#
|
211
|
+
# @param [String, #to_str] new_client_credential_key
|
212
|
+
# The client credential key.
|
213
|
+
def client_credential_key=(new_client_credential_key)
|
214
|
+
if new_client_credential_key != nil
|
215
|
+
if !new_client_credential_key.respond_to?(:to_str)
|
216
|
+
raise TypeError,
|
217
|
+
"Can't convert #{new_client_credential_key.class} into String."
|
218
|
+
end
|
219
|
+
new_client_credential_key = new_client_credential_key.to_str
|
220
|
+
@client_credential_key = new_client_credential_key
|
221
|
+
else
|
222
|
+
@client_credential_key = nil
|
223
|
+
end
|
224
|
+
end
|
225
|
+
alias_method :consumer_key=, :client_credential_key=
|
226
|
+
|
227
|
+
##
|
228
|
+
# Returns the client credential secret for this client.
|
229
|
+
#
|
230
|
+
# @return [String] The client credential secret.
|
231
|
+
def client_credential_secret
|
232
|
+
return @client_credential_secret
|
233
|
+
end
|
234
|
+
alias_method :consumer_secret, :client_credential_secret
|
235
|
+
|
236
|
+
##
|
237
|
+
# Sets the client credential secret for this client.
|
238
|
+
#
|
239
|
+
# @param [String, #to_str] new_client_credential_secret
|
240
|
+
# The client credential secret.
|
241
|
+
def client_credential_secret=(new_client_credential_secret)
|
242
|
+
if new_client_credential_secret != nil
|
243
|
+
if !new_client_credential_secret.respond_to?(:to_str)
|
244
|
+
raise TypeError,
|
245
|
+
"Can't convert #{new_client_credential_secret.class} " +
|
246
|
+
"into String."
|
247
|
+
end
|
248
|
+
new_client_credential_secret = new_client_credential_secret.to_str
|
249
|
+
@client_credential_secret = new_client_credential_secret
|
250
|
+
else
|
251
|
+
@client_credential_secret = nil
|
252
|
+
end
|
253
|
+
end
|
254
|
+
alias_method :consumer_secret=, :client_credential_secret=
|
255
|
+
|
256
|
+
##
|
257
|
+
# Returns the temporary credential for this client.
|
258
|
+
#
|
259
|
+
# @return [Signet::OAuth1::Credential] The temporary credentials.
|
260
|
+
def temporary_credential
|
261
|
+
if self.temporary_credential_key && self.temporary_credential_secret
|
262
|
+
return ::Signet::OAuth1::Credential.new(
|
263
|
+
self.temporary_credential_key,
|
264
|
+
self.temporary_credential_secret
|
265
|
+
)
|
266
|
+
elsif !self.temporary_credential_key &&
|
267
|
+
!self.temporary_credential_secret
|
268
|
+
return nil
|
269
|
+
else
|
270
|
+
raise ArgumentError,
|
271
|
+
"The temporary credential key and secret must be set."
|
272
|
+
end
|
273
|
+
end
|
274
|
+
alias_method :request_token, :temporary_credential
|
275
|
+
|
276
|
+
##
|
277
|
+
# Sets the temporary credential for this client.
|
278
|
+
#
|
279
|
+
# @param [Signet::OAuth1::Credential] new_temporary_credential
|
280
|
+
# The temporary credentials.
|
281
|
+
def temporary_credential=(new_temporary_credential)
|
282
|
+
if new_temporary_credential != nil
|
283
|
+
if !new_temporary_credential.kind_of?(::Signet::OAuth1::Credential)
|
284
|
+
raise TypeError,
|
285
|
+
"Expected Signet::OAuth1::Credential, " +
|
286
|
+
"got #{new_temporary_credential.class}."
|
287
|
+
end
|
288
|
+
@temporary_credential_key = new_temporary_credential.key
|
289
|
+
@temporary_credential_secret = new_temporary_credential.secret
|
290
|
+
else
|
291
|
+
@temporary_credential_key = nil
|
292
|
+
@temporary_credential_secret = nil
|
293
|
+
end
|
294
|
+
end
|
295
|
+
alias_method :request_token=, :temporary_credential=
|
296
|
+
|
297
|
+
##
|
298
|
+
# Returns the temporary credential key for this client.
|
299
|
+
#
|
300
|
+
# @return [String] The temporary credential key.
|
301
|
+
def temporary_credential_key
|
302
|
+
return @temporary_credential_key
|
303
|
+
end
|
304
|
+
alias_method :request_token_key, :temporary_credential_key
|
305
|
+
|
306
|
+
##
|
307
|
+
# Sets the temporary credential key for this client.
|
308
|
+
#
|
309
|
+
# @param [String, #to_str] new_temporary_credential_key
|
310
|
+
# The temporary credential key.
|
311
|
+
def temporary_credential_key=(new_temporary_credential_key)
|
312
|
+
if new_temporary_credential_key != nil
|
313
|
+
if !new_temporary_credential_key.respond_to?(:to_str)
|
314
|
+
raise TypeError,
|
315
|
+
"Can't convert #{new_temporary_credential_key.class} " +
|
316
|
+
"into String."
|
317
|
+
end
|
318
|
+
new_temporary_credential_key = new_temporary_credential_key.to_str
|
319
|
+
@temporary_credential_key = new_temporary_credential_key
|
320
|
+
else
|
321
|
+
@temporary_credential_key = nil
|
322
|
+
end
|
323
|
+
end
|
324
|
+
alias_method :request_token_key=, :temporary_credential_key=
|
325
|
+
|
326
|
+
##
|
327
|
+
# Returns the temporary credential secret for this client.
|
328
|
+
#
|
329
|
+
# @return [String] The temporary credential secret.
|
330
|
+
def temporary_credential_secret
|
331
|
+
return @temporary_credential_secret
|
332
|
+
end
|
333
|
+
alias_method :request_token_secret, :temporary_credential_secret
|
334
|
+
|
335
|
+
##
|
336
|
+
# Sets the temporary credential secret for this client.
|
337
|
+
#
|
338
|
+
# @param [String, #to_str] new_temporary_credential_secret
|
339
|
+
# The temporary credential secret.
|
340
|
+
def temporary_credential_secret=(new_temporary_credential_secret)
|
341
|
+
if new_temporary_credential_secret != nil
|
342
|
+
if !new_temporary_credential_secret.respond_to?(:to_str)
|
343
|
+
raise TypeError,
|
344
|
+
"Can't convert #{new_temporary_credential_secret.class} " +
|
345
|
+
"into String."
|
346
|
+
end
|
347
|
+
new_temporary_credential_secret =
|
348
|
+
new_temporary_credential_secret.to_str
|
349
|
+
@temporary_credential_secret = new_temporary_credential_secret
|
350
|
+
else
|
351
|
+
@temporary_credential_secret = nil
|
352
|
+
end
|
353
|
+
end
|
354
|
+
alias_method :request_token_secret=, :temporary_credential_secret=
|
355
|
+
|
356
|
+
##
|
357
|
+
# Returns the token credential for this client.
|
358
|
+
#
|
359
|
+
# @return [Signet::OAuth1::Credential] The token credentials.
|
360
|
+
def token_credential
|
361
|
+
if self.token_credential_key && self.token_credential_secret
|
362
|
+
return ::Signet::OAuth1::Credential.new(
|
363
|
+
self.token_credential_key,
|
364
|
+
self.token_credential_secret
|
365
|
+
)
|
366
|
+
elsif !self.token_credential_key &&
|
367
|
+
!self.token_credential_secret
|
368
|
+
return nil
|
369
|
+
else
|
370
|
+
raise ArgumentError,
|
371
|
+
"The token credential key and secret must be set."
|
372
|
+
end
|
373
|
+
end
|
374
|
+
alias_method :access_token, :token_credential
|
375
|
+
|
376
|
+
##
|
377
|
+
# Sets the token credential for this client.
|
378
|
+
#
|
379
|
+
# @param [Signet::OAuth1::Credential] new_token_credential
|
380
|
+
# The token credentials.
|
381
|
+
def token_credential=(new_token_credential)
|
382
|
+
if new_token_credential != nil
|
383
|
+
if !new_token_credential.kind_of?(::Signet::OAuth1::Credential)
|
384
|
+
raise TypeError,
|
385
|
+
"Expected Signet::OAuth1::Credential, " +
|
386
|
+
"got #{new_token_credential.class}."
|
387
|
+
end
|
388
|
+
@token_credential_key = new_token_credential.key
|
389
|
+
@token_credential_secret = new_token_credential.secret
|
390
|
+
else
|
391
|
+
@token_credential_key = nil
|
392
|
+
@token_credential_secret = nil
|
393
|
+
end
|
394
|
+
end
|
395
|
+
alias_method :access_token=, :token_credential=
|
396
|
+
|
397
|
+
##
|
398
|
+
# Returns the token credential key for this client.
|
399
|
+
#
|
400
|
+
# @return [String] The token credential key.
|
401
|
+
def token_credential_key
|
402
|
+
return @token_credential_key
|
403
|
+
end
|
404
|
+
alias_method :access_token_key, :token_credential_key
|
405
|
+
|
406
|
+
##
|
407
|
+
# Sets the token credential key for this client.
|
408
|
+
#
|
409
|
+
# @param [String, #to_str] new_token_credential_key
|
410
|
+
# The token credential key.
|
411
|
+
def token_credential_key=(new_token_credential_key)
|
412
|
+
if new_token_credential_key != nil
|
413
|
+
if !new_token_credential_key.respond_to?(:to_str)
|
414
|
+
raise TypeError,
|
415
|
+
"Can't convert #{new_token_credential_key.class} " +
|
416
|
+
"into String."
|
417
|
+
end
|
418
|
+
new_token_credential_key = new_token_credential_key.to_str
|
419
|
+
@token_credential_key = new_token_credential_key
|
420
|
+
else
|
421
|
+
@token_credential_key = nil
|
422
|
+
end
|
423
|
+
end
|
424
|
+
alias_method :access_token_key=, :token_credential_key=
|
425
|
+
|
426
|
+
##
|
427
|
+
# Returns the token credential secret for this client.
|
428
|
+
#
|
429
|
+
# @return [String] The token credential secret.
|
430
|
+
def token_credential_secret
|
431
|
+
return @token_credential_secret
|
432
|
+
end
|
433
|
+
alias_method :access_token_secret, :token_credential_secret
|
434
|
+
|
435
|
+
##
|
436
|
+
# Sets the token credential secret for this client.
|
437
|
+
#
|
438
|
+
# @param [String, #to_str] new_token_credential_secret
|
439
|
+
# The token credential secret.
|
440
|
+
def token_credential_secret=(new_token_credential_secret)
|
441
|
+
if new_token_credential_secret != nil
|
442
|
+
if !new_token_credential_secret.respond_to?(:to_str)
|
443
|
+
raise TypeError,
|
444
|
+
"Can't convert #{new_token_credential_secret.class} " +
|
445
|
+
"into String."
|
446
|
+
end
|
447
|
+
new_token_credential_secret =
|
448
|
+
new_token_credential_secret.to_str
|
449
|
+
@token_credential_secret = new_token_credential_secret
|
450
|
+
else
|
451
|
+
@token_credential_secret = nil
|
452
|
+
end
|
453
|
+
end
|
454
|
+
alias_method :access_token_secret=, :token_credential_secret=
|
455
|
+
|
456
|
+
##
|
457
|
+
# Returns the callback for this client.
|
458
|
+
#
|
459
|
+
# @return [String] The OAuth callback.
|
460
|
+
def callback
|
461
|
+
return @callback || ::Signet::OAuth1::OUT_OF_BAND
|
462
|
+
end
|
463
|
+
|
464
|
+
##
|
465
|
+
# Sets the callback for this client.
|
466
|
+
#
|
467
|
+
# @param [String, #to_str] new_callback
|
468
|
+
# The OAuth callback.
|
469
|
+
def callback=(new_callback)
|
470
|
+
if new_callback != nil
|
471
|
+
if !new_callback.respond_to?(:to_str)
|
472
|
+
raise TypeError,
|
473
|
+
"Can't convert #{new_callback.class} into String."
|
474
|
+
end
|
475
|
+
new_callback = new_callback.to_str
|
476
|
+
@callback = new_callback
|
477
|
+
else
|
478
|
+
@callback = nil
|
479
|
+
end
|
480
|
+
end
|
481
|
+
|
482
|
+
##
|
483
|
+
# Generates a request for temporary credentials.
|
484
|
+
#
|
485
|
+
# @param [Hash] options
|
486
|
+
# The configuration parameters for the request.
|
487
|
+
# - <code>:signature_method</code> —
|
488
|
+
# The signature method. Defaults to <code>'HMAC-SHA1'</code>.
|
489
|
+
# - <code>:additional_parameters</code> —
|
490
|
+
# Non-standard additional parameters.
|
491
|
+
# - <code>:realm</code> —
|
492
|
+
# The Authorization realm. See RFC 2617.
|
493
|
+
#
|
494
|
+
# @return [Array] The request object.
|
495
|
+
def generate_temporary_credential_request(options={})
|
496
|
+
verifications = {
|
497
|
+
:temporary_credential_uri => 'Temporary credentials URI',
|
498
|
+
:client_credential_key => 'Client credential key',
|
499
|
+
:client_credential_secret => 'Client credential secret'
|
500
|
+
}
|
501
|
+
# Make sure all required state is set
|
502
|
+
verifications.each do |(key, value)|
|
503
|
+
unless self.send(key)
|
504
|
+
raise ArgumentError, "#{key} was not set."
|
505
|
+
end
|
506
|
+
end
|
507
|
+
options = {
|
508
|
+
:signature_method => 'HMAC-SHA1',
|
509
|
+
:additional_parameters => [],
|
510
|
+
:realm => nil
|
511
|
+
}.merge(options)
|
512
|
+
method = 'POST'
|
513
|
+
parameters = ::Signet::OAuth1.unsigned_temporary_credential_parameters(
|
514
|
+
:client_credential_key => self.client_credential_key,
|
515
|
+
:callback => self.callback,
|
516
|
+
:signature_method => options[:signature_method],
|
517
|
+
:additional_parameters => options[:additional_parameters]
|
518
|
+
)
|
519
|
+
signature = ::Signet::OAuth1.sign_parameters(
|
520
|
+
method,
|
521
|
+
self.temporary_credential_uri,
|
522
|
+
parameters,
|
523
|
+
self.client_credential_secret
|
524
|
+
)
|
525
|
+
parameters << ['oauth_signature', signature]
|
526
|
+
authorization_header = [
|
527
|
+
'Authorization',
|
528
|
+
::Signet::OAuth1.generate_authorization_header(
|
529
|
+
parameters, options[:realm]
|
530
|
+
)
|
531
|
+
]
|
532
|
+
headers = [authorization_header]
|
533
|
+
if method == 'POST'
|
534
|
+
headers << ['Content-Type', 'application/x-www-form-urlencoded']
|
535
|
+
end
|
536
|
+
return [
|
537
|
+
method,
|
538
|
+
self.temporary_credential_uri.to_str,
|
539
|
+
headers,
|
540
|
+
['']
|
541
|
+
]
|
542
|
+
end
|
543
|
+
alias_method(
|
544
|
+
:generate_request_token_request,
|
545
|
+
:generate_temporary_credential_request
|
546
|
+
)
|
547
|
+
|
548
|
+
##
|
549
|
+
# Transmits a request for a temporary credential. This method does not
|
550
|
+
# have side-effects within the client.
|
551
|
+
#
|
552
|
+
# @param [Hash] options
|
553
|
+
# The configuration parameters for the request.
|
554
|
+
# - <code>:signature_method</code> —
|
555
|
+
# The signature method. Defaults to <code>'HMAC-SHA1'</code>.
|
556
|
+
# - <code>:additional_parameters</code> —
|
557
|
+
# Non-standard additional parameters.
|
558
|
+
# - <code>:realm</code> —
|
559
|
+
# The Authorization realm. See RFC 2617.
|
560
|
+
# - <code>:adapter</code> —
|
561
|
+
# The HTTP adapter.
|
562
|
+
# Defaults to <code>HTTPAdapter::NetHTTPRequestAdapter</code>.
|
563
|
+
# - <code>:connection</code> —
|
564
|
+
# An open, manually managed HTTP connection.
|
565
|
+
# Must be of type <code>HTTPAdapter::Connection</code> and the
|
566
|
+
# internal connection representation must match the HTTP adapter
|
567
|
+
# being used.
|
568
|
+
#
|
569
|
+
# @return [Signet::OAuth1::Credential] The temporary credential.
|
570
|
+
#
|
571
|
+
# @example
|
572
|
+
# temporary_credential = client.fetch_temporary_credential(
|
573
|
+
# :additional_parameters => {
|
574
|
+
# :scope => 'https://mail.google.com/mail/feed/atom'
|
575
|
+
# }
|
576
|
+
# )
|
577
|
+
def fetch_temporary_credential(options={})
|
578
|
+
adapter = options[:adapter]
|
579
|
+
unless adapter
|
580
|
+
require 'httpadapter'
|
581
|
+
require 'httpadapter/adapters/net_http'
|
582
|
+
adapter = HTTPAdapter::NetHTTPRequestAdapter
|
583
|
+
end
|
584
|
+
connection = options[:connection]
|
585
|
+
request = self.generate_temporary_credential_request(options)
|
586
|
+
response = HTTPAdapter.transmit(request, adapter, connection)
|
587
|
+
status, headers, body = response
|
588
|
+
merged_body = StringIO.new
|
589
|
+
body.each do |chunk|
|
590
|
+
merged_body.write(chunk)
|
591
|
+
end
|
592
|
+
body = merged_body.string
|
593
|
+
if status.to_i == 200
|
594
|
+
return ::Signet::OAuth1.parse_form_encoded_credentials(body)
|
595
|
+
elsif [400, 401, 403].include?(status.to_i)
|
596
|
+
message = 'Authorization failed.'
|
597
|
+
if body.strip.length > 0
|
598
|
+
message += " Server message:\n#{body.strip}"
|
599
|
+
end
|
600
|
+
error = ::Signet::AuthorizationError.new(message, request, response)
|
601
|
+
raise error
|
602
|
+
else
|
603
|
+
message = "Unexpected status code: #{status}."
|
604
|
+
if body.strip.length > 0
|
605
|
+
message += " Server message:\n#{body.strip}"
|
606
|
+
end
|
607
|
+
error = ::Signet::AuthorizationError.new(message, request, response)
|
608
|
+
raise error
|
609
|
+
end
|
610
|
+
end
|
611
|
+
alias_method(
|
612
|
+
:fetch_request_token,
|
613
|
+
:fetch_temporary_credential
|
614
|
+
)
|
615
|
+
|
616
|
+
##
|
617
|
+
# Transmits a request for a temporary credential. This method updates
|
618
|
+
# the client with the new temporary credential.
|
619
|
+
#
|
620
|
+
# @param [Hash] options
|
621
|
+
# The configuration parameters for the request.
|
622
|
+
# - <code>:signature_method</code> —
|
623
|
+
# The signature method. Defaults to <code>'HMAC-SHA1'</code>.
|
624
|
+
# - <code>:additional_parameters</code> —
|
625
|
+
# Non-standard additional parameters.
|
626
|
+
# - <code>:realm</code> —
|
627
|
+
# The Authorization realm. See RFC 2617.
|
628
|
+
# - <code>:adapter</code> —
|
629
|
+
# The HTTP adapter.
|
630
|
+
# Defaults to <code>HTTPAdapter::NetHTTPRequestAdapter</code>.
|
631
|
+
# - <code>:connection</code> —
|
632
|
+
# An open, manually managed HTTP connection.
|
633
|
+
# Must be of type <code>HTTPAdapter::Connection</code> and the
|
634
|
+
# internal connection representation must match the HTTP adapter
|
635
|
+
# being used.
|
636
|
+
#
|
637
|
+
# @return [Signet::OAuth1::Credential] The temporary credential.
|
638
|
+
#
|
639
|
+
# @example
|
640
|
+
# client.fetch_temporary_credential!(:additional_parameters => {
|
641
|
+
# :scope => 'https://mail.google.com/mail/feed/atom'
|
642
|
+
# })
|
643
|
+
def fetch_temporary_credential!(options={})
|
644
|
+
credential = self.fetch_temporary_credential(options)
|
645
|
+
self.temporary_credential = credential
|
646
|
+
end
|
647
|
+
alias_method(
|
648
|
+
:fetch_request_token!,
|
649
|
+
:fetch_temporary_credential!
|
650
|
+
)
|
651
|
+
|
652
|
+
##
|
653
|
+
# Generates a request for token credentials.
|
654
|
+
#
|
655
|
+
# @param [Hash] options
|
656
|
+
# The configuration parameters for the request.
|
657
|
+
# - <code>:verifier</code> —
|
658
|
+
# The OAuth verifier provided by the server. Required.
|
659
|
+
# - <code>:signature_method</code> —
|
660
|
+
# The signature method. Defaults to <code>'HMAC-SHA1'</code>.
|
661
|
+
# - <code>:realm</code> —
|
662
|
+
# The Authorization realm. See RFC 2617.
|
663
|
+
#
|
664
|
+
# @return [Array] The request object.
|
665
|
+
def generate_token_credential_request(options={})
|
666
|
+
verifications = {
|
667
|
+
:token_credential_uri => 'Token credentials URI',
|
668
|
+
:client_credential_key => 'Client credential key',
|
669
|
+
:client_credential_secret => 'Client credential secret',
|
670
|
+
:temporary_credential_key => 'Temporary credential key',
|
671
|
+
:temporary_credential_secret => 'Temporary credential secret'
|
672
|
+
}
|
673
|
+
# Make sure all required state is set
|
674
|
+
verifications.each do |(key, value)|
|
675
|
+
unless self.send(key)
|
676
|
+
raise ArgumentError, "#{key} was not set."
|
677
|
+
end
|
678
|
+
end
|
679
|
+
options = {
|
680
|
+
:signature_method => 'HMAC-SHA1',
|
681
|
+
:realm => nil
|
682
|
+
}.merge(options)
|
683
|
+
method = 'POST'
|
684
|
+
parameters = ::Signet::OAuth1.unsigned_token_credential_parameters(
|
685
|
+
:client_credential_key => self.client_credential_key,
|
686
|
+
:temporary_credential_key => self.temporary_credential_key,
|
687
|
+
:signature_method => options[:signature_method],
|
688
|
+
:verifier => options[:verifier]
|
689
|
+
)
|
690
|
+
signature = ::Signet::OAuth1.sign_parameters(
|
691
|
+
method,
|
692
|
+
self.token_credential_uri,
|
693
|
+
parameters,
|
694
|
+
self.client_credential_secret,
|
695
|
+
self.temporary_credential_secret
|
696
|
+
)
|
697
|
+
parameters << ['oauth_signature', signature]
|
698
|
+
authorization_header = [
|
699
|
+
'Authorization',
|
700
|
+
::Signet::OAuth1.generate_authorization_header(
|
701
|
+
parameters, options[:realm]
|
702
|
+
)
|
703
|
+
]
|
704
|
+
headers = [authorization_header]
|
705
|
+
if method == 'POST'
|
706
|
+
headers << ['Content-Type', 'application/x-www-form-urlencoded']
|
707
|
+
end
|
708
|
+
return [
|
709
|
+
method,
|
710
|
+
self.token_credential_uri.to_str,
|
711
|
+
headers,
|
712
|
+
['']
|
713
|
+
]
|
714
|
+
end
|
715
|
+
alias_method(
|
716
|
+
:generate_access_token_request,
|
717
|
+
:generate_token_credential_request
|
718
|
+
)
|
719
|
+
|
720
|
+
##
|
721
|
+
# Transmits a request for a token credential. This method does not
|
722
|
+
# have side-effects within the client.
|
723
|
+
#
|
724
|
+
# @param [Hash] options
|
725
|
+
# The configuration parameters for the request.
|
726
|
+
# - <code>:verifier</code> —
|
727
|
+
# The OAuth verifier provided by the server. Required.
|
728
|
+
# - <code>:signature_method</code> —
|
729
|
+
# The signature method. Defaults to <code>'HMAC-SHA1'</code>.
|
730
|
+
# - <code>:realm</code> —
|
731
|
+
# The Authorization realm. See RFC 2617.
|
732
|
+
# - <code>:adapter</code> —
|
733
|
+
# The HTTP adapter.
|
734
|
+
# Defaults to <code>HTTPAdapter::NetHTTPRequestAdapter</code>.
|
735
|
+
# - <code>:connection</code> —
|
736
|
+
# An open, manually managed HTTP connection.
|
737
|
+
# Must be of type <code>HTTPAdapter::Connection</code> and the
|
738
|
+
# internal connection representation must match the HTTP adapter
|
739
|
+
# being used.
|
740
|
+
#
|
741
|
+
# @return [Signet::OAuth1::Credential] The token credential.
|
742
|
+
#
|
743
|
+
# @example
|
744
|
+
# token_credential = client.fetch_token_credential(
|
745
|
+
# :verifier => '12345'
|
746
|
+
# )
|
747
|
+
def fetch_token_credential(options={})
|
748
|
+
adapter = options[:adapter]
|
749
|
+
unless adapter
|
750
|
+
require 'httpadapter'
|
751
|
+
require 'httpadapter/adapters/net_http'
|
752
|
+
adapter = HTTPAdapter::NetHTTPRequestAdapter
|
753
|
+
end
|
754
|
+
connection = options[:connection]
|
755
|
+
request = self.generate_token_credential_request(options)
|
756
|
+
response = HTTPAdapter.transmit(request, adapter, connection)
|
757
|
+
status, headers, body = response
|
758
|
+
merged_body = StringIO.new
|
759
|
+
body.each do |chunk|
|
760
|
+
merged_body.write(chunk)
|
761
|
+
end
|
762
|
+
body = merged_body.string
|
763
|
+
if status.to_i == 200
|
764
|
+
return ::Signet::OAuth1.parse_form_encoded_credentials(body)
|
765
|
+
elsif [400, 401, 403].include?(status.to_i)
|
766
|
+
message = 'Authorization failed.'
|
767
|
+
if body.strip.length > 0
|
768
|
+
message += " Server message:\n#{body.strip}"
|
769
|
+
end
|
770
|
+
error = ::Signet::AuthorizationError.new(message, request, response)
|
771
|
+
raise error
|
772
|
+
else
|
773
|
+
message = "Unexpected status code: #{status}."
|
774
|
+
if body.strip.length > 0
|
775
|
+
message += " Server message:\n#{body.strip}"
|
776
|
+
end
|
777
|
+
error = ::Signet::AuthorizationError.new(message, request, response)
|
778
|
+
raise error
|
779
|
+
end
|
780
|
+
end
|
781
|
+
alias_method(
|
782
|
+
:fetch_access_token,
|
783
|
+
:fetch_token_credential
|
784
|
+
)
|
785
|
+
|
786
|
+
##
|
787
|
+
# Transmits a request for a token credential. This method updates
|
788
|
+
# the client with the new token credential.
|
789
|
+
#
|
790
|
+
# @param [Hash] options
|
791
|
+
# The configuration parameters for the request.
|
792
|
+
# - <code>:signature_method</code> —
|
793
|
+
# The signature method. Defaults to <code>'HMAC-SHA1'</code>.
|
794
|
+
# - <code>:additional_parameters</code> —
|
795
|
+
# Non-standard additional parameters.
|
796
|
+
# - <code>:realm</code> —
|
797
|
+
# The Authorization realm. See RFC 2617.
|
798
|
+
# - <code>:adapter</code> —
|
799
|
+
# The HTTP adapter.
|
800
|
+
# Defaults to <code>HTTPAdapter::NetHTTPRequestAdapter</code>.
|
801
|
+
# - <code>:connection</code> —
|
802
|
+
# An open, manually managed HTTP connection.
|
803
|
+
# Must be of type <code>HTTPAdapter::Connection</code> and the
|
804
|
+
# internal connection representation must match the HTTP adapter
|
805
|
+
# being used.
|
806
|
+
#
|
807
|
+
# @return [Signet::OAuth1::Credential] The token credential.
|
808
|
+
#
|
809
|
+
# @example
|
810
|
+
# client.fetch_token_credential!(:verifier => '12345')
|
811
|
+
def fetch_token_credential!(options={})
|
812
|
+
credential = self.fetch_token_credential(options)
|
813
|
+
self.token_credential = credential
|
814
|
+
end
|
815
|
+
alias_method(
|
816
|
+
:fetch_access_token!,
|
817
|
+
:fetch_token_credential!
|
818
|
+
)
|
819
|
+
|
820
|
+
##
|
821
|
+
# Generates an authenticated request for protected resources.
|
822
|
+
#
|
823
|
+
# @param [Hash] options
|
824
|
+
# The configuration parameters for the request.
|
825
|
+
# - <code>:request</code> —
|
826
|
+
# A pre-constructed request to sign.
|
827
|
+
# - <code>:method</code> —
|
828
|
+
# The HTTP method for the request. Defaults to 'GET'.
|
829
|
+
# - <code>:uri</code> —
|
830
|
+
# The URI for the request.
|
831
|
+
# - <code>:headers</code> —
|
832
|
+
# The HTTP headers for the request.
|
833
|
+
# - <code>:body</code> —
|
834
|
+
# The HTTP body for the request.
|
835
|
+
# - <code>:signature_method</code> —
|
836
|
+
# The signature method. Defaults to <code>'HMAC-SHA1'</code>.
|
837
|
+
# - <code>:realm</code> —
|
838
|
+
# The Authorization realm. See RFC 2617.
|
839
|
+
#
|
840
|
+
# @return [Array] The request object.
|
841
|
+
def generate_authenticated_request(options={})
|
842
|
+
verifications = {
|
843
|
+
:client_credential_key => 'Client credential key',
|
844
|
+
:client_credential_secret => 'Client credential secret',
|
845
|
+
:token_credential_key => 'Token credential key',
|
846
|
+
:token_credential_secret => 'Token credential secret'
|
847
|
+
}
|
848
|
+
# Make sure all required state is set
|
849
|
+
verifications.each do |(key, value)|
|
850
|
+
unless self.send(key)
|
851
|
+
raise ArgumentError, "#{key} was not set."
|
852
|
+
end
|
853
|
+
end
|
854
|
+
options = {
|
855
|
+
:signature_method => 'HMAC-SHA1',
|
856
|
+
:realm => nil
|
857
|
+
}.merge(options)
|
858
|
+
if options[:request]
|
859
|
+
if options[:request].kind_of?(Array)
|
860
|
+
request = options[:request]
|
861
|
+
elsif options[:adapter] || options[:request].respond_to?(:to_ary)
|
862
|
+
request =
|
863
|
+
HTTPAdapter.adapt_request(options[:request], options[:adapter])
|
864
|
+
end
|
865
|
+
method, uri, headers, body = request
|
866
|
+
else
|
867
|
+
method = options[:method] || 'GET'
|
868
|
+
uri = options[:uri]
|
869
|
+
headers = options[:headers] || []
|
870
|
+
body = options[:body] || ''
|
871
|
+
end
|
872
|
+
request_components = {
|
873
|
+
:method => method,
|
874
|
+
:uri => uri,
|
875
|
+
:headers => headers,
|
876
|
+
:body => body
|
877
|
+
}
|
878
|
+
# Verify that we have all pieces required to return an HTTP request
|
879
|
+
request_components.each do |(key, value)|
|
880
|
+
unless value
|
881
|
+
raise ArgumentError, "Missing :#{key} parameter."
|
882
|
+
end
|
883
|
+
end
|
884
|
+
if !body.kind_of?(String) && body.respond_to?(:each)
|
885
|
+
# Just in case we get a chunked body
|
886
|
+
merged_body = StringIO.new
|
887
|
+
body.each do |chunk|
|
888
|
+
merged_body.write(chunk)
|
889
|
+
end
|
890
|
+
body = merged_body.string
|
891
|
+
end
|
892
|
+
if !body.kind_of?(String)
|
893
|
+
raise TypeError, "Expected String, got #{body.class}."
|
894
|
+
end
|
895
|
+
method = method.to_s.upcase
|
896
|
+
parameters = ::Signet::OAuth1.unsigned_resource_parameters(
|
897
|
+
:client_credential_key => self.client_credential_key,
|
898
|
+
:token_credential_key => self.token_credential_key,
|
899
|
+
:signature_method => options[:signature_method]
|
900
|
+
)
|
901
|
+
media_type = nil
|
902
|
+
headers.each do |(header, value)|
|
903
|
+
if header.downcase == 'Content-Type'.downcase
|
904
|
+
media_type = value.gsub(/^([^;]+)(;.*?)?$/, '\1')
|
905
|
+
end
|
906
|
+
end
|
907
|
+
if method == 'POST' &&
|
908
|
+
media_type == 'application/x-www-form-urlencoded'
|
909
|
+
post_parameters = Addressable::URI.form_unencode(body)
|
910
|
+
else
|
911
|
+
post_parameters = []
|
912
|
+
end
|
913
|
+
parameters = parameters.concat(post_parameters)
|
914
|
+
# No need to attach URI query parameters, the .sign_parameters
|
915
|
+
# method takes care of that automatically.
|
916
|
+
signature = ::Signet::OAuth1.sign_parameters(
|
917
|
+
method,
|
918
|
+
uri,
|
919
|
+
parameters,
|
920
|
+
self.client_credential_secret,
|
921
|
+
self.token_credential_secret
|
922
|
+
)
|
923
|
+
parameters << ['oauth_signature', signature]
|
924
|
+
authorization_header = [
|
925
|
+
'Authorization',
|
926
|
+
::Signet::OAuth1.generate_authorization_header(
|
927
|
+
parameters, options[:realm]
|
928
|
+
)
|
929
|
+
]
|
930
|
+
headers << authorization_header
|
931
|
+
return [method, uri.to_str, headers, [body]]
|
932
|
+
end
|
933
|
+
|
934
|
+
##
|
935
|
+
# Transmits a request for a protected resource.
|
936
|
+
#
|
937
|
+
# @param [Hash] options
|
938
|
+
# The configuration parameters for the request.
|
939
|
+
# - <code>:request</code> —
|
940
|
+
# A pre-constructed request to sign.
|
941
|
+
# - <code>:method</code> —
|
942
|
+
# The HTTP method for the request. Defaults to 'GET'.
|
943
|
+
# - <code>:uri</code> —
|
944
|
+
# The URI for the request.
|
945
|
+
# - <code>:headers</code> —
|
946
|
+
# The HTTP headers for the request.
|
947
|
+
# - <code>:body</code> —
|
948
|
+
# The HTTP body for the request.
|
949
|
+
# - <code>:signature_method</code> —
|
950
|
+
# The signature method. Defaults to <code>'HMAC-SHA1'</code>.
|
951
|
+
# - <code>:realm</code> —
|
952
|
+
# The Authorization realm. See RFC 2617.
|
953
|
+
# - <code>:adapter</code> —
|
954
|
+
# The HTTP adapter.
|
955
|
+
# Defaults to <code>HTTPAdapter::NetHTTPRequestAdapter</code>.
|
956
|
+
# - <code>:connection</code> —
|
957
|
+
# An open, manually managed HTTP connection.
|
958
|
+
# Must be of type <code>HTTPAdapter::Connection</code> and the
|
959
|
+
# internal connection representation must match the HTTP adapter
|
960
|
+
# being used.
|
961
|
+
#
|
962
|
+
# @example
|
963
|
+
# # Using Net::HTTP
|
964
|
+
# response = client.fetch_protected_resource(
|
965
|
+
# :uri => 'http://www.example.com/protected/resource'
|
966
|
+
# )
|
967
|
+
# status, headers, body = response
|
968
|
+
#
|
969
|
+
# @example
|
970
|
+
# # Using Typhoeus
|
971
|
+
# response = client.fetch_protected_resource(
|
972
|
+
# :request => Typhoeus::Request.new(
|
973
|
+
# 'http://www.example.com/protected/resource'
|
974
|
+
# ),
|
975
|
+
# :adapter => HTTPAdapter::TyphoeusRequestAdapter,
|
976
|
+
# :connection => connection
|
977
|
+
# )
|
978
|
+
# status, headers, body = response
|
979
|
+
#
|
980
|
+
# @return [Array] The response object.
|
981
|
+
def fetch_protected_resource(options={})
|
982
|
+
adapter = options[:adapter]
|
983
|
+
unless adapter
|
984
|
+
require 'httpadapter'
|
985
|
+
require 'httpadapter/adapters/net_http'
|
986
|
+
adapter = HTTPAdapter::NetHTTPRequestAdapter
|
987
|
+
end
|
988
|
+
connection = options[:connection]
|
989
|
+
request = self.generate_authenticated_request(options)
|
990
|
+
response = HTTPAdapter.transmit(request, adapter, connection)
|
991
|
+
status, headers, body = response
|
992
|
+
merged_body = StringIO.new
|
993
|
+
body.each do |chunk|
|
994
|
+
merged_body.write(chunk)
|
995
|
+
end
|
996
|
+
body = merged_body.string
|
997
|
+
if status.to_i == 401
|
998
|
+
# When accessing a protected resource, we only want to raise an
|
999
|
+
# error for 401 responses.
|
1000
|
+
message = 'Authorization failed.'
|
1001
|
+
if body.strip.length > 0
|
1002
|
+
message += " Server message:\n#{body.strip}"
|
1003
|
+
end
|
1004
|
+
error = ::Signet::AuthorizationError.new(message, request, response)
|
1005
|
+
raise error
|
1006
|
+
else
|
1007
|
+
return response
|
1008
|
+
end
|
1009
|
+
end
|
1010
|
+
end
|
1011
|
+
end
|
1012
|
+
end
|