simple_oauth 0.1.6 → 0.1.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +2 -2
- data/.travis.yml +1 -0
- data/Gemfile +1 -1
- data/{LICENSE.md → LICENSE} +3 -1
- data/README.md +8 -9
- data/Rakefile +4 -20
- data/lib/simple_oauth.rb +0 -5
- data/lib/simple_oauth/header.rb +13 -7
- data/simple_oauth.gemspec +14 -14
- data/spec/simple_oauth/header_spec.rb +354 -0
- data/{test/helper.rb → spec/spec_helper.rb} +2 -4
- data/{test/rsa_private_key → spec/support/fixtures/rsa-private-key} +0 -0
- data/spec/support/rsa.rb +11 -0
- metadata +13 -47
- data/init.rb +0 -1
- data/lib/simple_oauth/core_ext/object.rb +0 -10
- data/lib/simple_oauth/version.rb +0 -8
- data/test/simple_oauth_test.rb +0 -309
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
data/Gemfile
CHANGED
data/{LICENSE.md → LICENSE}
RENAMED
data/README.md
CHANGED
@@ -2,16 +2,14 @@
|
|
2
2
|
|
3
3
|
Simply builds and verifies OAuth headers
|
4
4
|
|
5
|
-
## <a name="pulls"></a>
|
5
|
+
## <a name="pulls"></a>Contributing
|
6
6
|
1. Fork the project.
|
7
7
|
2. Create a topic branch.
|
8
|
-
3.
|
9
|
-
4. Add
|
10
|
-
5. Run
|
11
|
-
6.
|
12
|
-
7.
|
13
|
-
8. Commit and push your changes.
|
14
|
-
9. Submit a pull request. Please do not include changes to the gemspec or version file. (If you want to create your own version for some reason, please do so in a separate commit.)
|
8
|
+
3. Add failing tests.
|
9
|
+
4. Add code to pass the failing tests.
|
10
|
+
5. Run `bundle exec rake`. If failing, repeat step 4.
|
11
|
+
6. Commit and push your changes.
|
12
|
+
7. Submit a pull request. Please do not include changes to the gemspec.
|
15
13
|
|
16
14
|
## <a name="rubies"></a>Supported Rubies
|
17
15
|
This library aims to support and is [tested
|
@@ -21,6 +19,7 @@ implementations:
|
|
21
19
|
* Ruby 1.8.7
|
22
20
|
* Ruby 1.9.2
|
23
21
|
* Ruby 1.9.3
|
22
|
+
# Ruby head
|
24
23
|
* [JRuby](http://www.jruby.org/)
|
25
24
|
* [Rubinius](http://rubini.us/)
|
26
25
|
|
@@ -40,4 +39,4 @@ time of a major release, support for that Ruby version may be dropped.
|
|
40
39
|
|
41
40
|
## <a name="copyright"></a>Copyright
|
42
41
|
Copyright (c) 2010 Steve Richert, Erik Michaels-Ober.
|
43
|
-
See [LICENSE](https://github.com/laserlemon/simple_oauth/blob/master/LICENSE
|
42
|
+
See [LICENSE](https://github.com/laserlemon/simple_oauth/blob/master/LICENSE) for details.
|
data/Rakefile
CHANGED
@@ -1,24 +1,8 @@
|
|
1
1
|
#!/usr/bin/env rake
|
2
2
|
|
3
|
-
require 'bundler'
|
4
|
-
|
3
|
+
require 'bundler/gem_tasks'
|
4
|
+
require 'rspec/core/rake_task'
|
5
5
|
|
6
|
-
|
7
|
-
Rake::TestTask.new do |test|
|
8
|
-
test.libs << 'lib' << 'test'
|
9
|
-
test.pattern = 'test/**/*_test.rb'
|
10
|
-
test.verbose = true
|
11
|
-
end
|
6
|
+
RSpec::Core::RakeTask.new(:spec)
|
12
7
|
|
13
|
-
task :default => :
|
14
|
-
|
15
|
-
namespace :doc do
|
16
|
-
require 'yard'
|
17
|
-
YARD::Rake::YardocTask.new do |task|
|
18
|
-
task.files = ['README.md', 'LICENSE.md', 'lib/**/*.rb']
|
19
|
-
task.options = [
|
20
|
-
'--output-dir', 'doc/yard',
|
21
|
-
'--markup', 'markdown',
|
22
|
-
]
|
23
|
-
end
|
24
|
-
end
|
8
|
+
task :default => :spec
|
data/lib/simple_oauth.rb
CHANGED
data/lib/simple_oauth/header.rb
CHANGED
@@ -1,3 +1,8 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
require 'uri'
|
3
|
+
require 'base64'
|
4
|
+
require 'cgi'
|
5
|
+
|
1
6
|
module SimpleOAuth
|
2
7
|
class Header
|
3
8
|
ATTRIBUTE_KEYS = [:consumer_key, :nonce, :signature_method, :timestamp, :token, :version] unless defined? ::SimpleOAuth::Header::ATTRIBUTE_KEYS
|
@@ -30,17 +35,18 @@ module SimpleOAuth
|
|
30
35
|
|
31
36
|
def initialize(method, url, params, oauth = {})
|
32
37
|
@method = method.to_s.upcase
|
33
|
-
@uri = URI.parse(url.to_s)
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
end
|
38
|
+
@uri = URI.parse(url.to_s)
|
39
|
+
@uri.scheme = @uri.scheme.downcase
|
40
|
+
@uri.normalize!
|
41
|
+
@uri.fragment = nil
|
38
42
|
@params = params
|
39
43
|
@options = oauth.is_a?(Hash) ? self.class.default_options.merge(oauth) : self.class.parse(oauth)
|
40
44
|
end
|
41
45
|
|
42
46
|
def url
|
43
|
-
|
47
|
+
uri = @uri.dup
|
48
|
+
uri.query = nil
|
49
|
+
uri.to_s
|
44
50
|
end
|
45
51
|
|
46
52
|
def to_s
|
@@ -95,7 +101,7 @@ module SimpleOAuth
|
|
95
101
|
end
|
96
102
|
|
97
103
|
def url_params
|
98
|
-
CGI.parse(@uri.query || '').inject([]){|p,(k,vs)| p + vs.map{|v| [k, v] } }
|
104
|
+
CGI.parse(@uri.query || '').inject([]){|p,(k,vs)| p + vs.sort.map{|v| [k, v] } }
|
99
105
|
end
|
100
106
|
|
101
107
|
def rsa_sha1_signature
|
data/simple_oauth.gemspec
CHANGED
@@ -1,20 +1,20 @@
|
|
1
1
|
# encoding: utf-8
|
2
|
-
require File.expand_path('../lib/simple_oauth/version', __FILE__)
|
3
2
|
|
4
3
|
Gem::Specification.new do |gem|
|
5
|
-
gem.
|
6
|
-
gem.
|
4
|
+
gem.name = 'simple_oauth'
|
5
|
+
gem.version = '0.1.7'
|
6
|
+
|
7
|
+
gem.authors = ["Steve Richert", "Erik Michaels-Ober"]
|
8
|
+
gem.email = ['steve.richert@gmail.com', 'sferik@gmail.com']
|
9
|
+
gem.description = 'Simply builds and verifies OAuth headers'
|
10
|
+
gem.summary = gem.description
|
11
|
+
gem.homepage = 'https://github.com/laserlemon/simple_oauth'
|
12
|
+
|
7
13
|
gem.add_development_dependency 'rake'
|
14
|
+
gem.add_development_dependency 'rspec', '~> 2.0'
|
8
15
|
gem.add_development_dependency 'simplecov'
|
9
|
-
|
10
|
-
gem.
|
11
|
-
gem.
|
12
|
-
gem.
|
13
|
-
gem.files = `git ls-files`.split("\n")
|
14
|
-
gem.homepage = 'http://github.com/laserlemon/simple_oauth'
|
15
|
-
gem.name = 'simple_oauth'
|
16
|
-
gem.required_rubygems_version = Gem::Requirement.new('>= 1.3.6')
|
17
|
-
gem.summary = gem.description
|
18
|
-
gem.test_files = `git ls-files -- test/**/*_test.rb`.split("\n")
|
19
|
-
gem.version = SimpleOAuth::Version::STRING
|
16
|
+
|
17
|
+
gem.files = `git ls-files`.split($\)
|
18
|
+
gem.test_files = gem.files.grep(/^test\//)
|
19
|
+
gem.require_paths = ["lib"]
|
20
20
|
end
|
@@ -0,0 +1,354 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe SimpleOAuth::Header do
|
6
|
+
describe '.default_options' do
|
7
|
+
let(:default_options){ SimpleOAuth::Header.default_options }
|
8
|
+
|
9
|
+
it 'is different every time' do
|
10
|
+
SimpleOAuth::Header.default_options.should_not == default_options
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'is used for new headers' do
|
14
|
+
SimpleOAuth::Header.stub(:default_options => default_options)
|
15
|
+
header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friendships.json', {})
|
16
|
+
header.options.should == default_options
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'includes a signature method and an OAuth version' do
|
20
|
+
default_options[:signature_method].should_not be_nil
|
21
|
+
default_options[:version].should_not be_nil
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe '.encode' do
|
26
|
+
it 'encodes (most) non-word characters' do
|
27
|
+
[' ', '!', '@', '#', '$', '%', '^', '&'].each do |character|
|
28
|
+
encoded = SimpleOAuth::Header.encode(character)
|
29
|
+
encoded.should_not == character
|
30
|
+
encoded.should == URI.encode(character, /.*/)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'does not encode - . or ~' do
|
35
|
+
['-', '.', '~'].each do |character|
|
36
|
+
encoded = SimpleOAuth::Header.encode(character)
|
37
|
+
encoded.should == character
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.test_special_characters
|
42
|
+
it 'encodes non-ASCII characters' do
|
43
|
+
SimpleOAuth::Header.encode('é').should == '%C3%A9'
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'encodes multibyte characters' do
|
47
|
+
SimpleOAuth::Header.encode('あ').should == '%E3%81%82'
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
if RUBY_VERSION >= '1.9'
|
52
|
+
test_special_characters
|
53
|
+
else
|
54
|
+
%w(n N e E s S u U).each do |kcode|
|
55
|
+
describe %(when $KCODE = "#{kcode}") do
|
56
|
+
original_kcode = $KCODE
|
57
|
+
begin
|
58
|
+
$KCODE = kcode
|
59
|
+
test_special_characters
|
60
|
+
ensure
|
61
|
+
$KCODE = original_kcode
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
describe '.decode' do
|
69
|
+
pending
|
70
|
+
end
|
71
|
+
|
72
|
+
describe '.parse' do
|
73
|
+
let(:header){ SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friends.json', {}) }
|
74
|
+
let(:parsed_options){ SimpleOAuth::Header.parse(header) }
|
75
|
+
|
76
|
+
it 'returns a hash' do
|
77
|
+
parsed_options.should be_a(Hash)
|
78
|
+
end
|
79
|
+
|
80
|
+
it 'includes the options used to build the header' do
|
81
|
+
parsed_options.reject{|k,_| k == :signature }.should == header.options
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'includes a signature' do
|
85
|
+
header.options.should_not have_key(:signature)
|
86
|
+
parsed_options.should have_key(:signature)
|
87
|
+
parsed_options[:signature].should_not be_nil
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
describe '#initialize' do
|
92
|
+
let(:header){ SimpleOAuth::Header.new(:get, 'HTTPS://api.TWITTER.com:443/1/statuses/friendships.json?foo=bar#anchor', {}) }
|
93
|
+
|
94
|
+
it 'stringifies and uppercases the request method' do
|
95
|
+
header.method.should == 'GET'
|
96
|
+
end
|
97
|
+
|
98
|
+
it 'downcases the scheme and authority' do
|
99
|
+
header.url.should =~ %r(^https://api\.twitter\.com/)
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'ignores the query and fragment' do
|
103
|
+
header.url.should =~ %r(/1/statuses/friendships\.json$)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
describe '#valid?' do
|
108
|
+
context 'using the HMAC-SHA1 signature method' do
|
109
|
+
it 'requires consumer and token secrets' do
|
110
|
+
secrets = {:consumer_secret => 'CONSUMER_SECRET', :token_secret => 'TOKEN_SECRET'}
|
111
|
+
header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friends.json', {}, secrets)
|
112
|
+
parsed_header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friends.json', {}, header)
|
113
|
+
parsed_header.should_not be_valid
|
114
|
+
parsed_header.should be_valid(secrets)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
context 'using the RSA-SHA1 signature method' do
|
119
|
+
it 'requires an identical private key' do
|
120
|
+
secrets = {:consumer_secret => rsa_private_key}
|
121
|
+
header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friends.json', {}, secrets.merge(:signature_method => 'RSA-SHA1'))
|
122
|
+
parsed_header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friends.json', {}, header)
|
123
|
+
expect{ parsed_header.valid? }.to raise_error(TypeError)
|
124
|
+
parsed_header.should be_valid(secrets)
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
context 'using the RSA-SHA1 signature method' do
|
129
|
+
it 'requires consumer and token secrets' do
|
130
|
+
secrets = {:consumer_secret => 'CONSUMER_SECRET', :token_secret => 'TOKEN_SECRET'}
|
131
|
+
header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friends.json', {}, secrets.merge(:signature_method => 'PLAINTEXT'))
|
132
|
+
parsed_header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friends.json', {}, header)
|
133
|
+
parsed_header.should_not be_valid
|
134
|
+
parsed_header.should be_valid(secrets)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
describe '#normalized_attributes' do
|
140
|
+
let(:header){ SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friends.json', {}) }
|
141
|
+
let(:normalized_attributes){ header.send(:normalized_attributes) }
|
142
|
+
|
143
|
+
it 'returns a sorted-key, quoted-value and comma-separated list' do
|
144
|
+
header.stub(:signed_attributes => {:d => 1, :c => 2, :b => 3, :a => 4})
|
145
|
+
normalized_attributes.should == 'a="4", b="3", c="2", d="1"'
|
146
|
+
end
|
147
|
+
|
148
|
+
it 'url-encodes its values' do
|
149
|
+
header.stub(:signed_attributes => {1 => '!', 2 => '@', 3 => '#', 4 => '$'})
|
150
|
+
normalized_attributes.should == '1="%21", 2="%40", 3="%23", 4="%24"'
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
describe '#signed_attributes' do
|
155
|
+
it 'includes the OAuth signature' do
|
156
|
+
header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friends.json', {})
|
157
|
+
header.send(:signed_attributes).should have_key(:oauth_signature)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
describe '#attributes' do
|
162
|
+
let(:header) do
|
163
|
+
options = {}
|
164
|
+
SimpleOAuth::Header::ATTRIBUTE_KEYS.each{|k| options[k] = k.to_s.upcase }
|
165
|
+
options[:other] = 'OTHER'
|
166
|
+
SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friendships.json', {}, options)
|
167
|
+
end
|
168
|
+
let(:attributes){ header.send(:attributes) }
|
169
|
+
|
170
|
+
it 'prepends keys with "oauth_"' do
|
171
|
+
attributes.keys.should be_all{|k| k.to_s =~ /^oauth_/ }
|
172
|
+
end
|
173
|
+
|
174
|
+
it 'excludes keys not included in the list of valid attributes' do
|
175
|
+
attributes.keys.should be_all{|k| k.is_a?(Symbol) }
|
176
|
+
attributes.should_not have_key(:oauth_other)
|
177
|
+
end
|
178
|
+
|
179
|
+
it 'preserves values for valid keys' do
|
180
|
+
attributes.size.should == SimpleOAuth::Header::ATTRIBUTE_KEYS.size
|
181
|
+
attributes.should be_all{|k,v| k.to_s == "oauth_#{v.downcase}" }
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
describe '#signature' do
|
186
|
+
context 'calls the appropriate signature method' do
|
187
|
+
specify 'when using HMAC-SHA1' do
|
188
|
+
header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friends.json', {}, :signature_method => 'HMAC-SHA1')
|
189
|
+
header.should_receive(:hmac_sha1_signature).once.and_return('HMAC_SHA1_SIGNATURE')
|
190
|
+
header.send(:signature).should == 'HMAC_SHA1_SIGNATURE'
|
191
|
+
end
|
192
|
+
|
193
|
+
specify 'when using RSA-SHA1' do
|
194
|
+
header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friends.json', {}, :signature_method => 'RSA-SHA1')
|
195
|
+
header.should_receive(:rsa_sha1_signature).once.and_return('RSA_SHA1_SIGNATURE')
|
196
|
+
header.send(:signature).should == 'RSA_SHA1_SIGNATURE'
|
197
|
+
end
|
198
|
+
|
199
|
+
specify 'when using PLAINTEXT' do
|
200
|
+
header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friends.json', {}, :signature_method => 'PLAINTEXT')
|
201
|
+
header.should_receive(:plaintext_signature).once.and_return('PLAINTEXT_SIGNATURE')
|
202
|
+
header.send(:signature).should == 'PLAINTEXT_SIGNATURE'
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
describe '#hmac_sha1_signature' do
|
208
|
+
it 'reproduces a successful Twitter GET' do
|
209
|
+
options = {
|
210
|
+
:consumer_key => '8karQBlMg6gFOwcf8kcoYw',
|
211
|
+
:consumer_secret => '3d0vcHyUiiqADpWxolW8nlDIpSWMlyK7YNgc5Qna2M',
|
212
|
+
:nonce => '547fed103e122eecf84c080843eedfe6',
|
213
|
+
:signature_method => 'HMAC-SHA1',
|
214
|
+
:timestamp => '1286830180',
|
215
|
+
:token => '201425800-Sv4sTcgoffmHGkTCue0JnURT8vrm4DiFAkeFNDkh',
|
216
|
+
:token_secret => 'T5qa1tF57tfDzKmpM89DHsNuhgOY4NT6DlNLsTFcuQ'
|
217
|
+
}
|
218
|
+
header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friends.json', {}, options)
|
219
|
+
header.to_s.should == 'OAuth oauth_consumer_key="8karQBlMg6gFOwcf8kcoYw", oauth_nonce="547fed103e122eecf84c080843eedfe6", oauth_signature="i9CT6ahDRAlfGX3hKYf78QzXsaw%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1286830180", oauth_token="201425800-Sv4sTcgoffmHGkTCue0JnURT8vrm4DiFAkeFNDkh", oauth_version="1.0"'
|
220
|
+
end
|
221
|
+
|
222
|
+
it 'reproduces a successful Twitter POST' do
|
223
|
+
options = {
|
224
|
+
:consumer_key => '8karQBlMg6gFOwcf8kcoYw',
|
225
|
+
:consumer_secret => '3d0vcHyUiiqADpWxolW8nlDIpSWMlyK7YNgc5Qna2M',
|
226
|
+
:nonce => 'b40a3e0f18590ecdcc0e273f7d7c82f8',
|
227
|
+
:signature_method => 'HMAC-SHA1',
|
228
|
+
:timestamp => '1286830181',
|
229
|
+
:token => '201425800-Sv4sTcgoffmHGkTCue0JnURT8vrm4DiFAkeFNDkh',
|
230
|
+
:token_secret => 'T5qa1tF57tfDzKmpM89DHsNuhgOY4NT6DlNLsTFcuQ'
|
231
|
+
}
|
232
|
+
header = SimpleOAuth::Header.new(:post, 'https://api.twitter.com/1/statuses/update.json', {:status => 'hi, again'}, options)
|
233
|
+
header.to_s.should == 'OAuth oauth_consumer_key="8karQBlMg6gFOwcf8kcoYw", oauth_nonce="b40a3e0f18590ecdcc0e273f7d7c82f8", oauth_signature="mPqSFKejrWWk3ZT9bTQjhO5b2xI%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1286830181", oauth_token="201425800-Sv4sTcgoffmHGkTCue0JnURT8vrm4DiFAkeFNDkh", oauth_version="1.0"'
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
describe '#secret' do
|
238
|
+
let(:header){ SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friendships.json', {}) }
|
239
|
+
let(:secret){ header.send(:secret) }
|
240
|
+
|
241
|
+
it 'combines the consumer and token secrets with an ampersand' do
|
242
|
+
header.stub(:options => {:consumer_secret => 'CONSUMER_SECRET', :token_secret => 'TOKEN_SECRET'})
|
243
|
+
secret.should == 'CONSUMER_SECRET&TOKEN_SECRET'
|
244
|
+
end
|
245
|
+
|
246
|
+
it 'URL encodes each secret value before combination' do
|
247
|
+
header.stub(:options => {:consumer_secret => 'CONSUM#R_SECRET', :token_secret => 'TOKEN_S#CRET'})
|
248
|
+
secret.should == 'CONSUM%23R_SECRET&TOKEN_S%23CRET'
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
describe '#signature_base' do
|
253
|
+
let(:header){ SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friendships.json', {}) }
|
254
|
+
let(:signature_base){ header.send(:signature_base) }
|
255
|
+
|
256
|
+
it 'combines the request method, URL and normalized parameters using ampersands' do
|
257
|
+
header.stub(:method => 'METHOD', :url => 'URL', :normalized_params => 'NORMALIZED_PARAMS')
|
258
|
+
signature_base.should == 'METHOD&URL&NORMALIZED_PARAMS'
|
259
|
+
end
|
260
|
+
|
261
|
+
it 'URL encodes each value before combination' do
|
262
|
+
header.stub(:method => 'ME#HOD', :url => 'U#L', :normalized_params => 'NORMAL#ZED_PARAMS')
|
263
|
+
signature_base.should == 'ME%23HOD&U%23L&NORMAL%23ZED_PARAMS'
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
describe '#normalized_params' do
|
268
|
+
let(:header) do
|
269
|
+
header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friendships.json', {})
|
270
|
+
header.stub(:signature_params => [['A', '4'], ['B', '3'], ['B', '2'], ['C', '1'], ['D[]', '0 ']])
|
271
|
+
header
|
272
|
+
end
|
273
|
+
let(:signature_params){ header.send(:signature_params) }
|
274
|
+
let(:normalized_params){ header.send(:normalized_params) }
|
275
|
+
|
276
|
+
it 'joins key/value pairs with equal signs and ampersands' do
|
277
|
+
normalized_params.should be_a(String)
|
278
|
+
parts = normalized_params.split('&')
|
279
|
+
parts.size.should == signature_params.size
|
280
|
+
pairs = parts.map{|p| p.split('=') }
|
281
|
+
pairs.should be_all{|p| p.size == 2 }
|
282
|
+
end
|
283
|
+
end
|
284
|
+
|
285
|
+
describe '#signature_params' do
|
286
|
+
let(:header){ SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friendships.json', {}) }
|
287
|
+
let(:signature_params){ header.send(:signature_params) }
|
288
|
+
|
289
|
+
it 'combines OAuth header attributes, body parameters and URL parameters into an flattened array of key/value pairs' do
|
290
|
+
header.stub(
|
291
|
+
:attributes => {:attribute => 'ATTRIBUTE'},
|
292
|
+
:params => {'param' => 'PARAM'},
|
293
|
+
:url_params => [['url_param', '1'], ['url_param', '2']]
|
294
|
+
)
|
295
|
+
signature_params.should == [
|
296
|
+
[:attribute, 'ATTRIBUTE'],
|
297
|
+
['param', 'PARAM'],
|
298
|
+
['url_param', '1'],
|
299
|
+
['url_param', '2']
|
300
|
+
]
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
304
|
+
describe '#url_params' do
|
305
|
+
it 'returns an empty array when the URL has no query parameters' do
|
306
|
+
header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friendships.json', {})
|
307
|
+
header.send(:url_params).should == []
|
308
|
+
end
|
309
|
+
|
310
|
+
it 'returns an array of key/value pairs for each query parameter' do
|
311
|
+
header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friendships.json?test=TEST', {})
|
312
|
+
header.send(:url_params).should == [['test', 'TEST']]
|
313
|
+
end
|
314
|
+
|
315
|
+
it 'sorts values for repeated keys' do
|
316
|
+
header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friendships.json?test=3&test=1&test=2', {})
|
317
|
+
header.send(:url_params).should == [['test', '1'], ['test', '2'], ['test', '3']]
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
describe '#rsa_sha1_signature' do
|
322
|
+
it 'reproduces a successful OAuth example GET' do
|
323
|
+
options = {
|
324
|
+
:consumer_key => 'dpf43f3p2l4k3l03',
|
325
|
+
:consumer_secret => rsa_private_key,
|
326
|
+
:nonce => '13917289812797014437',
|
327
|
+
:signature_method => 'RSA-SHA1',
|
328
|
+
:timestamp => '1196666512'
|
329
|
+
}
|
330
|
+
header = SimpleOAuth::Header.new(:get, 'http://photos.example.net/photos', {:file => 'vacaction.jpg', :size => 'original'}, options)
|
331
|
+
header.to_s.should == 'OAuth oauth_consumer_key="dpf43f3p2l4k3l03", oauth_nonce="13917289812797014437", oauth_signature="jvTp%2FwX1TYtByB1m%2BPbyo0lnCOLIsyGCH7wke8AUs3BpnwZJtAuEJkvQL2%2F9n4s5wUmUl4aCI4BwpraNx4RtEXMe5qg5T1LVTGliMRpKasKsW%2F%2Fe%2BRinhejgCuzoH26dyF8iY2ZZ%2F5D1ilgeijhV%2FvBka5twt399mXwaYdCwFYE%3D", oauth_signature_method="RSA-SHA1", oauth_timestamp="1196666512", oauth_version="1.0"'
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
describe '#private_key' do
|
336
|
+
pending
|
337
|
+
end
|
338
|
+
|
339
|
+
describe '#plaintext_signature' do
|
340
|
+
it 'reproduces a successful OAuth example GET' do
|
341
|
+
options = {
|
342
|
+
:consumer_key => 'abcd',
|
343
|
+
:consumer_secret => 'efgh',
|
344
|
+
:nonce => 'oLKtec51GQy',
|
345
|
+
:signature_method => 'PLAINTEXT',
|
346
|
+
:timestamp => '1286977095',
|
347
|
+
:token => 'ijkl',
|
348
|
+
:token_secret => 'mnop'
|
349
|
+
}
|
350
|
+
header = SimpleOAuth::Header.new(:get, 'http://host.net/resource?name=value', {:name => 'value'}, options)
|
351
|
+
header.to_s.should == 'OAuth oauth_consumer_key="abcd", oauth_nonce="oLKtec51GQy", oauth_signature="efgh%26mnop", oauth_signature_method="PLAINTEXT", oauth_timestamp="1286977095", oauth_token="ijkl", oauth_version="1.0"'
|
352
|
+
end
|
353
|
+
end
|
354
|
+
end
|
File without changes
|
data/spec/support/rsa.rb
ADDED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: simple_oauth
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.7
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -13,7 +13,7 @@ cert_chain: []
|
|
13
13
|
date: 2012-04-23 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
|
-
name:
|
16
|
+
name: rake
|
17
17
|
requirement: !ruby/object:Gem::Requirement
|
18
18
|
none: false
|
19
19
|
requirements:
|
@@ -29,13 +29,13 @@ dependencies:
|
|
29
29
|
- !ruby/object:Gem::Version
|
30
30
|
version: '0'
|
31
31
|
- !ruby/object:Gem::Dependency
|
32
|
-
name:
|
32
|
+
name: rspec
|
33
33
|
requirement: !ruby/object:Gem::Requirement
|
34
34
|
none: false
|
35
35
|
requirements:
|
36
36
|
- - ~>
|
37
37
|
- !ruby/object:Gem::Version
|
38
|
-
version:
|
38
|
+
version: '2.0'
|
39
39
|
type: :development
|
40
40
|
prerelease: false
|
41
41
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -43,23 +43,7 @@ dependencies:
|
|
43
43
|
requirements:
|
44
44
|
- - ~>
|
45
45
|
- !ruby/object:Gem::Version
|
46
|
-
version:
|
47
|
-
- !ruby/object:Gem::Dependency
|
48
|
-
name: rake
|
49
|
-
requirement: !ruby/object:Gem::Requirement
|
50
|
-
none: false
|
51
|
-
requirements:
|
52
|
-
- - ! '>='
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version: '0'
|
55
|
-
type: :development
|
56
|
-
prerelease: false
|
57
|
-
version_requirements: !ruby/object:Gem::Requirement
|
58
|
-
none: false
|
59
|
-
requirements:
|
60
|
-
- - ! '>='
|
61
|
-
- !ruby/object:Gem::Version
|
62
|
-
version: '0'
|
46
|
+
version: '2.0'
|
63
47
|
- !ruby/object:Gem::Dependency
|
64
48
|
name: simplecov
|
65
49
|
requirement: !ruby/object:Gem::Requirement
|
@@ -76,22 +60,6 @@ dependencies:
|
|
76
60
|
- - ! '>='
|
77
61
|
- !ruby/object:Gem::Version
|
78
62
|
version: '0'
|
79
|
-
- !ruby/object:Gem::Dependency
|
80
|
-
name: yard
|
81
|
-
requirement: !ruby/object:Gem::Requirement
|
82
|
-
none: false
|
83
|
-
requirements:
|
84
|
-
- - ! '>='
|
85
|
-
- !ruby/object:Gem::Version
|
86
|
-
version: '0'
|
87
|
-
type: :development
|
88
|
-
prerelease: false
|
89
|
-
version_requirements: !ruby/object:Gem::Requirement
|
90
|
-
none: false
|
91
|
-
requirements:
|
92
|
-
- - ! '>='
|
93
|
-
- !ruby/object:Gem::Version
|
94
|
-
version: '0'
|
95
63
|
description: Simply builds and verifies OAuth headers
|
96
64
|
email:
|
97
65
|
- steve.richert@gmail.com
|
@@ -105,19 +73,17 @@ files:
|
|
105
73
|
- .travis.yml
|
106
74
|
- .yardopts
|
107
75
|
- Gemfile
|
108
|
-
- LICENSE
|
76
|
+
- LICENSE
|
109
77
|
- README.md
|
110
78
|
- Rakefile
|
111
|
-
- init.rb
|
112
79
|
- lib/simple_oauth.rb
|
113
|
-
- lib/simple_oauth/core_ext/object.rb
|
114
80
|
- lib/simple_oauth/header.rb
|
115
|
-
- lib/simple_oauth/version.rb
|
116
81
|
- simple_oauth.gemspec
|
117
|
-
-
|
118
|
-
-
|
119
|
-
-
|
120
|
-
|
82
|
+
- spec/simple_oauth/header_spec.rb
|
83
|
+
- spec/spec_helper.rb
|
84
|
+
- spec/support/fixtures/rsa-private-key
|
85
|
+
- spec/support/rsa.rb
|
86
|
+
homepage: https://github.com/laserlemon/simple_oauth
|
121
87
|
licenses: []
|
122
88
|
post_install_message:
|
123
89
|
rdoc_options: []
|
@@ -134,10 +100,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
134
100
|
requirements:
|
135
101
|
- - ! '>='
|
136
102
|
- !ruby/object:Gem::Version
|
137
|
-
version:
|
103
|
+
version: '0'
|
138
104
|
requirements: []
|
139
105
|
rubyforge_project:
|
140
|
-
rubygems_version: 1.8.
|
106
|
+
rubygems_version: 1.8.21
|
141
107
|
signing_key:
|
142
108
|
specification_version: 3
|
143
109
|
summary: Simply builds and verifies OAuth headers
|
data/init.rb
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
require File.expand_path('../lib/simple_oauth', __FILE__)
|
data/lib/simple_oauth/version.rb
DELETED
@@ -1,8 +0,0 @@
|
|
1
|
-
module SimpleOAuth
|
2
|
-
module Version
|
3
|
-
MAJOR = 0 unless defined? ::SimpleOAuth::Version::MAJOR
|
4
|
-
MINOR = 1 unless defined? ::SimpleOAuth::Version::MINOR
|
5
|
-
PATCH = 6 unless defined? ::SimpleOAuth::Version::PATCH
|
6
|
-
STRING = [MAJOR, MINOR, PATCH].join('.') unless defined? ::SimpleOAuth::Version::STRING
|
7
|
-
end
|
8
|
-
end
|
data/test/simple_oauth_test.rb
DELETED
@@ -1,309 +0,0 @@
|
|
1
|
-
# -*- encoding: utf-8 -*-
|
2
|
-
require 'helper'
|
3
|
-
|
4
|
-
class SimpleOAuthTest < Test::Unit::TestCase
|
5
|
-
def test_default_options
|
6
|
-
# Default header options should change with each call due to generation of
|
7
|
-
# a unique "timestamp" and "nonce" value combination.
|
8
|
-
default_options = SimpleOAuth::Header.default_options
|
9
|
-
assert_not_equal default_options, SimpleOAuth::Header.default_options
|
10
|
-
|
11
|
-
SimpleOAuth::Header.stubs(:default_options).returns(default_options)
|
12
|
-
header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friendships.json', {})
|
13
|
-
|
14
|
-
# Given no options argument, header options defer to the default options.
|
15
|
-
assert_equal default_options, header.options
|
16
|
-
|
17
|
-
# Default options should include a signature method and the OAuth version.
|
18
|
-
assert_equal 'HMAC-SHA1', default_options[:signature_method]
|
19
|
-
assert_equal '1.0', default_options[:version]
|
20
|
-
end
|
21
|
-
|
22
|
-
def test_encode
|
23
|
-
# Non-word characters should be URL encoded...
|
24
|
-
[' ', '!', '@', '#', '$', '%', '^', '&'].each do |character|
|
25
|
-
encoded = SimpleOAuth::Header.encode(character)
|
26
|
-
assert_not_equal character, encoded
|
27
|
-
assert_equal URI.encode(character, /.*/), encoded
|
28
|
-
end
|
29
|
-
|
30
|
-
# ...except for the "-", "." and "~" characters.
|
31
|
-
['-', '.', '~'].each do |character|
|
32
|
-
assert_equal character, SimpleOAuth::Header.encode(character)
|
33
|
-
end
|
34
|
-
|
35
|
-
major, minor, patch = RUBY_VERSION.split('.')
|
36
|
-
new_ruby = major.to_i >= 2 || major.to_i == 1 && minor.to_i >= 9
|
37
|
-
old_kcode = $KCODE if !new_ruby
|
38
|
-
begin
|
39
|
-
%w(n N e E s S u U).each do |kcode|
|
40
|
-
$KCODE = kcode if !new_ruby
|
41
|
-
assert_equal '%E3%81%82', SimpleOAuth::Header.encode('あ'), "Failed to correctly escape Japanese under $KCODE = #{kcode}"
|
42
|
-
assert_equal '%C3%A9', SimpleOAuth::Header.encode('é'), "Failed to correctly escape e+acute under $KCODE = #{kcode}"
|
43
|
-
end
|
44
|
-
ensure
|
45
|
-
$KCODE = old_kcode if !new_ruby
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
def test_decode
|
50
|
-
# Pending
|
51
|
-
end
|
52
|
-
|
53
|
-
def test_parse
|
54
|
-
header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friends.json', {})
|
55
|
-
parsed_options = SimpleOAuth::Header.parse(header)
|
56
|
-
|
57
|
-
# Parsed options should be a Hash.
|
58
|
-
assert_kind_of Hash, parsed_options
|
59
|
-
|
60
|
-
# Parsed options should equal the options used to build the header, along
|
61
|
-
# with the additional signature.
|
62
|
-
assert_equal header.options, parsed_options.reject{|k,v| k == :signature }
|
63
|
-
end
|
64
|
-
|
65
|
-
def test_initialize
|
66
|
-
header = SimpleOAuth::Header.new(:get, 'HTTPS://api.TWITTER.com:443/1/statuses/friendships.json#anchor', {})
|
67
|
-
|
68
|
-
# HTTP method should be an uppercase string.
|
69
|
-
#
|
70
|
-
# See: http://oauth.net/core/1.0/#rfc.section.9.1.3
|
71
|
-
assert_equal 'GET', header.method
|
72
|
-
|
73
|
-
# Request URL should downcase the scheme and authority parts as well as
|
74
|
-
# remove the query and fragment parts.
|
75
|
-
#
|
76
|
-
# See: http://oauth.net/core/1.0/#rfc.section.9.1.2
|
77
|
-
assert_equal 'https://api.twitter.com/1/statuses/friendships.json', header.url
|
78
|
-
end
|
79
|
-
|
80
|
-
def test_url
|
81
|
-
# Pending
|
82
|
-
end
|
83
|
-
|
84
|
-
def test_to_s
|
85
|
-
header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friends.json', {})
|
86
|
-
assert_equal "OAuth #{header.send(:normalized_attributes)}", header.to_s
|
87
|
-
end
|
88
|
-
|
89
|
-
def test_valid?
|
90
|
-
# When given consumer and token secrets, those secrets must be passed into
|
91
|
-
# the parsed header validation in order for the validity check to pass.
|
92
|
-
secrets = {:consumer_secret => 'CONSUMER_SECRET', :token_secret => 'TOKEN_SECRET'}
|
93
|
-
header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friends.json', {}, secrets)
|
94
|
-
parsed_header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friends.json', {}, header)
|
95
|
-
assert !parsed_header.valid?
|
96
|
-
assert parsed_header.valid?(secrets)
|
97
|
-
|
98
|
-
# Using the RSA-SHA1 signature method, the consumer secret must be a valid
|
99
|
-
# RSA private key. When parsing the header on the server side, the same
|
100
|
-
# consumer secret must be included in order for the header to validate.
|
101
|
-
secrets = {:consumer_secret => File.read('test/rsa_private_key')}
|
102
|
-
header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friends.json', {}, secrets.merge(:signature_method => 'RSA-SHA1'))
|
103
|
-
parsed_header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friends.json', {}, header)
|
104
|
-
assert_raise(TypeError){ parsed_header.valid? }
|
105
|
-
assert parsed_header.valid?(secrets)
|
106
|
-
|
107
|
-
# Like the default HMAC-RSA1 signature method, the PLAINTEXT method
|
108
|
-
# requires use of both a consumer secret and a token secret. A parsed
|
109
|
-
# header will not validate without these secret values.
|
110
|
-
secrets = {:consumer_secret => 'CONSUMER_SECRET', :token_secret => 'TOKEN_SECRET'}
|
111
|
-
header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friends.json', {}, secrets.merge(:signature_method => 'PLAINTEXT'))
|
112
|
-
parsed_header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friends.json', {}, header)
|
113
|
-
assert !parsed_header.valid?
|
114
|
-
assert parsed_header.valid?(secrets)
|
115
|
-
end
|
116
|
-
|
117
|
-
def test_normalized_attributes
|
118
|
-
header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friends.json', {})
|
119
|
-
header.stubs(:signed_attributes).returns(:d => 1, :c => 2, :b => 3, :a => 4)
|
120
|
-
|
121
|
-
# Should return the OAuth header attributes, sorted by name, with quoted
|
122
|
-
# values and comma-separated.
|
123
|
-
assert_equal 'a="4", b="3", c="2", d="1"', header.send(:normalized_attributes)
|
124
|
-
|
125
|
-
# Values should also be URL encoded.
|
126
|
-
header.stubs(:signed_attributes).returns(1 => '!', 2 => '@', 3 => '#', 4 => '$')
|
127
|
-
assert_equal '1="%21", 2="%40", 3="%23", 4="%24"', header.send(:normalized_attributes)
|
128
|
-
end
|
129
|
-
|
130
|
-
def test_signed_attributes
|
131
|
-
header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friends.json', {})
|
132
|
-
assert header.send(:signed_attributes).keys.include?(:oauth_signature)
|
133
|
-
end
|
134
|
-
|
135
|
-
def test_attributes
|
136
|
-
attribute_options = SimpleOAuth::Header::ATTRIBUTE_KEYS.inject({}){|o,a| o.merge(a => a.to_s.upcase) }
|
137
|
-
options = attribute_options.merge(:other => 'OTHER')
|
138
|
-
header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friendships.json', {}, options)
|
139
|
-
attributes = header.send(:attributes)
|
140
|
-
|
141
|
-
# OAuth header attributes are all to begin with the "oauth_" prefix.
|
142
|
-
assert attributes.all?{|k,v| k.to_s =~ /^oauth_/ }
|
143
|
-
|
144
|
-
# Custom options not included in the list of valid attribute keys should
|
145
|
-
# not be included in the header attributes.
|
146
|
-
assert !attributes.key?(:oauth_other)
|
147
|
-
|
148
|
-
# Valid attribute option values should be preserved.
|
149
|
-
assert_equal attribute_options.size, attributes.size
|
150
|
-
assert attributes.all?{|k,v| k.to_s == "oauth_#{v.downcase}" }
|
151
|
-
end
|
152
|
-
|
153
|
-
def test_signature
|
154
|
-
header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friends.json', {}, :signature_method => 'HMAC-SHA1')
|
155
|
-
header.expects(:hmac_sha1_signature).once.returns('HMAC_SHA1_SIGNATURE')
|
156
|
-
assert_equal 'HMAC_SHA1_SIGNATURE', header.send(:signature)
|
157
|
-
|
158
|
-
header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friends.json', {}, :signature_method => 'RSA-SHA1')
|
159
|
-
header.expects(:rsa_sha1_signature).once.returns('RSA_SHA1_SIGNATURE')
|
160
|
-
assert_equal 'RSA_SHA1_SIGNATURE', header.send(:signature)
|
161
|
-
|
162
|
-
header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friends.json', {}, :signature_method => 'PLAINTEXT')
|
163
|
-
header.expects(:plaintext_signature).once.returns('PLAINTEXT_SIGNATURE')
|
164
|
-
assert_equal 'PLAINTEXT_SIGNATURE', header.send(:signature)
|
165
|
-
end
|
166
|
-
|
167
|
-
def test_hmac_sha1_signature
|
168
|
-
# Reproduce an actual successful call to the Twitter API using the
|
169
|
-
# HMAC-SHA1 signature method, GETting a list of friends.
|
170
|
-
options = {
|
171
|
-
:consumer_key => '8karQBlMg6gFOwcf8kcoYw',
|
172
|
-
:consumer_secret => '3d0vcHyUiiqADpWxolW8nlDIpSWMlyK7YNgc5Qna2M',
|
173
|
-
:nonce => '547fed103e122eecf84c080843eedfe6',
|
174
|
-
#:signature_method => 'HMAC-SHA1',
|
175
|
-
:timestamp => '1286830180',
|
176
|
-
:token => '201425800-Sv4sTcgoffmHGkTCue0JnURT8vrm4DiFAkeFNDkh',
|
177
|
-
:token_secret => 'T5qa1tF57tfDzKmpM89DHsNuhgOY4NT6DlNLsTFcuQ'
|
178
|
-
}
|
179
|
-
successful = 'OAuth oauth_consumer_key="8karQBlMg6gFOwcf8kcoYw", oauth_nonce="547fed103e122eecf84c080843eedfe6", oauth_signature="i9CT6ahDRAlfGX3hKYf78QzXsaw%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1286830180", oauth_token="201425800-Sv4sTcgoffmHGkTCue0JnURT8vrm4DiFAkeFNDkh", oauth_version="1.0"'
|
180
|
-
header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friends.json', {}, options)
|
181
|
-
assert_equal successful, header.to_s
|
182
|
-
|
183
|
-
# Reproduce a successful Twitter call, POSTing a new status.
|
184
|
-
options.merge!(
|
185
|
-
:nonce => 'b40a3e0f18590ecdcc0e273f7d7c82f8',
|
186
|
-
:timestamp => '1286830181'
|
187
|
-
)
|
188
|
-
successful = 'OAuth oauth_consumer_key="8karQBlMg6gFOwcf8kcoYw", oauth_nonce="b40a3e0f18590ecdcc0e273f7d7c82f8", oauth_signature="mPqSFKejrWWk3ZT9bTQjhO5b2xI%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1286830181", oauth_token="201425800-Sv4sTcgoffmHGkTCue0JnURT8vrm4DiFAkeFNDkh", oauth_version="1.0"'
|
189
|
-
header = SimpleOAuth::Header.new(:post, 'https://api.twitter.com/1/statuses/update.json', {:status => 'hi, again'}, options)
|
190
|
-
assert_equal successful, header.to_s
|
191
|
-
end
|
192
|
-
|
193
|
-
def test_secret
|
194
|
-
header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friendships.json', {})
|
195
|
-
header.stubs(:options).returns(:consumer_secret => 'CONSUMER_SECRET', :token_secret => 'TOKEN_SECRET')
|
196
|
-
|
197
|
-
# Should combine the consumer and token secrets with an ampersand.
|
198
|
-
assert_equal 'CONSUMER_SECRET&TOKEN_SECRET', header.send(:secret)
|
199
|
-
|
200
|
-
header.stubs(:options).returns(:consumer_secret => 'CONSUM#R_SECRET', :token_secret => 'TOKEN_S#CRET')
|
201
|
-
|
202
|
-
# Should URL encode each secret value before combination.
|
203
|
-
assert_equal 'CONSUM%23R_SECRET&TOKEN_S%23CRET', header.send(:secret)
|
204
|
-
end
|
205
|
-
|
206
|
-
def test_signature_base
|
207
|
-
header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friendships.json', {})
|
208
|
-
header.stubs(:method).returns('METHOD')
|
209
|
-
header.stubs(:url).returns('URL')
|
210
|
-
header.stubs(:normalized_params).returns('NORMALIZED_PARAMS')
|
211
|
-
|
212
|
-
# Should combine HTTP method, URL and normalized parameters string using
|
213
|
-
# ampersands.
|
214
|
-
assert_equal 'METHOD&URL&NORMALIZED_PARAMS', header.send(:signature_base)
|
215
|
-
|
216
|
-
header.stubs(:method).returns('ME#HOD')
|
217
|
-
header.stubs(:url).returns('U#L')
|
218
|
-
header.stubs(:normalized_params).returns('NORMAL#ZED_PARAMS')
|
219
|
-
|
220
|
-
# Each of the three combined values should be URL encoded.
|
221
|
-
assert_equal 'ME%23HOD&U%23L&NORMAL%23ZED_PARAMS', header.send(:signature_base)
|
222
|
-
end
|
223
|
-
|
224
|
-
def test_normalized_params
|
225
|
-
header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friendships.json', {})
|
226
|
-
header.stubs(:signature_params).returns([['A', '4'], ['B', '3'], ['B', '2'], ['C', '1'], ['D[]', '0 ']])
|
227
|
-
|
228
|
-
# The +normalized_params+ string should join key=value pairs with
|
229
|
-
# ampersands.
|
230
|
-
signature_params = header.send(:signature_params)
|
231
|
-
normalized_params = header.send(:normalized_params)
|
232
|
-
parts = normalized_params.split('&')
|
233
|
-
pairs = parts.map{|p| p.split('=') }
|
234
|
-
assert_kind_of String, normalized_params
|
235
|
-
assert_equal signature_params.size, parts.size
|
236
|
-
assert pairs.all?{|p| p.size == 2 }
|
237
|
-
|
238
|
-
# The signature parameters should be sorted and the keys/values URL encoded
|
239
|
-
# first.
|
240
|
-
assert_equal signature_params.sort_by{|p| p.to_s}, pairs.map{|k, v| [URI.decode(k), URI.decode(v)]}
|
241
|
-
end
|
242
|
-
|
243
|
-
def test_signature_params
|
244
|
-
header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friendships.json', {})
|
245
|
-
header.stubs(:attributes).returns(:attribute => 'ATTRIBUTE')
|
246
|
-
header.stubs(:params).returns('param' => 'PARAM')
|
247
|
-
header.stubs(:url_params).returns([['url_param', '1'], ['url_param', '2']])
|
248
|
-
|
249
|
-
# Should combine OAuth header attributes, body parameters and URL
|
250
|
-
# parameters into an array of key value pairs.
|
251
|
-
signature_params = header.send(:signature_params)
|
252
|
-
assert_kind_of Array, signature_params
|
253
|
-
assert_equal [:attribute, 'param', 'url_param', 'url_param'], signature_params.map{|p| p.first}
|
254
|
-
assert_equal ['ATTRIBUTE', 'PARAM', '1', '2'], signature_params.map{|p| p.last}
|
255
|
-
end
|
256
|
-
|
257
|
-
def test_url_params
|
258
|
-
# A URL with no query parameters should produce empty +url_params+
|
259
|
-
header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friendships.json', {})
|
260
|
-
assert_equal [], header.send(:url_params)
|
261
|
-
|
262
|
-
# A URL with query parameters should return a hash having array values
|
263
|
-
# containing the given query parameters.
|
264
|
-
header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friendships.json?test=TEST', {})
|
265
|
-
url_params = header.send(:url_params)
|
266
|
-
assert_kind_of Array, url_params
|
267
|
-
assert_equal [['test', 'TEST']], url_params
|
268
|
-
|
269
|
-
# If a query parameter is repeated, the values should be sorted.
|
270
|
-
header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friendships.json?test=1&test=2', {})
|
271
|
-
assert_equal [['test', '1'], ['test', '2']], header.send(:url_params)
|
272
|
-
end
|
273
|
-
|
274
|
-
def test_rsa_sha1_signature
|
275
|
-
# Sample request taken from:
|
276
|
-
# http://wiki.oauth.net/TestCases
|
277
|
-
options = {
|
278
|
-
:consumer_key => 'dpf43f3p2l4k3l03',
|
279
|
-
:consumer_secret => File.read('test/rsa_private_key'),
|
280
|
-
:nonce => '13917289812797014437',
|
281
|
-
:signature_method => 'RSA-SHA1',
|
282
|
-
:timestamp => '1196666512'
|
283
|
-
}
|
284
|
-
successful = 'OAuth oauth_consumer_key="dpf43f3p2l4k3l03", oauth_nonce="13917289812797014437", oauth_signature="jvTp%2FwX1TYtByB1m%2BPbyo0lnCOLIsyGCH7wke8AUs3BpnwZJtAuEJkvQL2%2F9n4s5wUmUl4aCI4BwpraNx4RtEXMe5qg5T1LVTGliMRpKasKsW%2F%2Fe%2BRinhejgCuzoH26dyF8iY2ZZ%2F5D1ilgeijhV%2FvBka5twt399mXwaYdCwFYE%3D", oauth_signature_method="RSA-SHA1", oauth_timestamp="1196666512", oauth_version="1.0"'
|
285
|
-
header = SimpleOAuth::Header.new(:get, 'http://photos.example.net/photos', {:file => 'vacaction.jpg', :size => 'original'}, options)
|
286
|
-
assert_equal successful, header.to_s
|
287
|
-
end
|
288
|
-
|
289
|
-
def test_private_key
|
290
|
-
# Pending
|
291
|
-
end
|
292
|
-
|
293
|
-
def plaintext_signature
|
294
|
-
# Sample request taken from:
|
295
|
-
# http://oauth.googlecode.com/svn/code/javascript/example/signature.html
|
296
|
-
options = {
|
297
|
-
:consumer_key => 'abcd',
|
298
|
-
:consumer_secret => 'efgh',
|
299
|
-
:nonce => 'oLKtec51GQy',
|
300
|
-
:signature_method => 'PLAINTEXT',
|
301
|
-
:timestamp => '1286977095',
|
302
|
-
:token => 'ijkl',
|
303
|
-
:token_secret => 'mnop'
|
304
|
-
}
|
305
|
-
successful = 'OAuth oauth_consumer_key="abcd", oauth_nonce="oLKtec51GQy", oauth_signature="efgh%26mnop", oauth_signature_method="PLAINTEXT", oauth_timestamp="1286977095", oauth_token="ijkl", oauth_version="1.0"'
|
306
|
-
header = SimpleOAuth::Header.new(:get, 'http://host.net/resource?name=value', {:name => 'value'}, options)
|
307
|
-
assert_equal successful, header.to_s
|
308
|
-
end
|
309
|
-
end
|