simple_oauth 0.1.0 → 0.1.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.
- data/.gitignore +1 -0
- data/Gemfile +2 -0
- data/Gemfile.lock +18 -0
- data/lib/simple_oauth.rb +14 -10
- data/simple_oauth.gemspec +56 -0
- data/test/rsa_private_key +16 -0
- data/test/simple_oauth_test.rb +194 -159
- metadata +11 -3
data/.gitignore
CHANGED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
data/lib/simple_oauth.rb
CHANGED
@@ -7,7 +7,7 @@ module SimpleOAuth
|
|
7
7
|
module Version
|
8
8
|
MAJOR = 0
|
9
9
|
MINOR = 1
|
10
|
-
PATCH =
|
10
|
+
PATCH = 1
|
11
11
|
STRING = [MAJOR, MINOR, PATCH].join('.')
|
12
12
|
end
|
13
13
|
|
@@ -69,7 +69,7 @@ module SimpleOAuth
|
|
69
69
|
|
70
70
|
private
|
71
71
|
def normalized_attributes
|
72
|
-
signed_attributes.sort_by
|
72
|
+
signed_attributes.sort_by{|k,v| k.to_s }.map{|k,v| %(#{k}="#{self.class.encode(v)}") }.join(', ')
|
73
73
|
end
|
74
74
|
|
75
75
|
def signed_attributes
|
@@ -85,25 +85,19 @@ module SimpleOAuth
|
|
85
85
|
end
|
86
86
|
|
87
87
|
def hmac_sha1_signature
|
88
|
-
Base64.encode64(OpenSSL::HMAC.digest(OpenSSL::Digest::
|
89
|
-
end
|
90
|
-
|
91
|
-
def rsa_sha1_signature
|
92
|
-
Base64.encode64(OpenSSL::HMAC.digest(OpenSSL::Digest::Digest.new('sha1'), options[:private_key].to_s, signature_base)).chomp
|
88
|
+
Base64.encode64(OpenSSL::HMAC.digest(OpenSSL::Digest::SHA1.new, secret, signature_base)).chomp.gsub(/\n/, '')
|
93
89
|
end
|
94
90
|
|
95
91
|
def secret
|
96
92
|
options.values_at(:consumer_secret, :token_secret).map{|v| self.class.encode(v) }.join('&')
|
97
93
|
end
|
98
94
|
|
99
|
-
alias_method :plaintext_signature, :secret
|
100
|
-
|
101
95
|
def signature_base
|
102
96
|
[method, url, normalized_params].map{|v| self.class.encode(v) }.join('&')
|
103
97
|
end
|
104
98
|
|
105
99
|
def normalized_params
|
106
|
-
signature_params.
|
100
|
+
signature_params.map{|p| p.map{|v| self.class.encode(v) } }.sort.map{|p| p.join('=') }.join('&')
|
107
101
|
end
|
108
102
|
|
109
103
|
def signature_params
|
@@ -113,5 +107,15 @@ module SimpleOAuth
|
|
113
107
|
def url_params
|
114
108
|
CGI.parse(@uri.query || '').inject([]){|p,(k,vs)| p + vs.map{|v| [k, v] } }
|
115
109
|
end
|
110
|
+
|
111
|
+
def rsa_sha1_signature
|
112
|
+
Base64.encode64(private_key.sign(OpenSSL::Digest::SHA1.new, signature_base)).chomp.gsub(/\n/, '')
|
113
|
+
end
|
114
|
+
|
115
|
+
def private_key
|
116
|
+
OpenSSL::PKey::RSA.new(options[:consumer_secret])
|
117
|
+
end
|
118
|
+
|
119
|
+
alias_method :plaintext_signature, :secret
|
116
120
|
end
|
117
121
|
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{simple_oauth}
|
8
|
+
s.version = "0.1.1"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Steve Richert"]
|
12
|
+
s.date = %q{2010-10-13}
|
13
|
+
s.description = %q{Simply builds and verifies OAuth headers}
|
14
|
+
s.email = %q{steve.richert@gmail.com}
|
15
|
+
s.extra_rdoc_files = [
|
16
|
+
"LICENSE",
|
17
|
+
"README.rdoc"
|
18
|
+
]
|
19
|
+
s.files = [
|
20
|
+
".gitignore",
|
21
|
+
"Gemfile",
|
22
|
+
"Gemfile.lock",
|
23
|
+
"LICENSE",
|
24
|
+
"README.rdoc",
|
25
|
+
"Rakefile",
|
26
|
+
"init.rb",
|
27
|
+
"lib/simple_oauth.rb",
|
28
|
+
"simple_oauth.gemspec",
|
29
|
+
"test/helper.rb",
|
30
|
+
"test/rsa_private_key",
|
31
|
+
"test/simple_oauth_test.rb"
|
32
|
+
]
|
33
|
+
s.homepage = %q{http://github.com/laserlemon/simple_oauth}
|
34
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
35
|
+
s.require_paths = ["lib"]
|
36
|
+
s.rubygems_version = %q{1.3.7}
|
37
|
+
s.summary = %q{Simply builds and verifies OAuth headers}
|
38
|
+
s.test_files = [
|
39
|
+
"test/helper.rb",
|
40
|
+
"test/simple_oauth_test.rb"
|
41
|
+
]
|
42
|
+
|
43
|
+
if s.respond_to? :specification_version then
|
44
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
45
|
+
s.specification_version = 3
|
46
|
+
|
47
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
48
|
+
s.add_development_dependency(%q<mocha>, [">= 0"])
|
49
|
+
else
|
50
|
+
s.add_dependency(%q<mocha>, [">= 0"])
|
51
|
+
end
|
52
|
+
else
|
53
|
+
s.add_dependency(%q<mocha>, [">= 0"])
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
@@ -0,0 +1,16 @@
|
|
1
|
+
-----BEGIN PRIVATE KEY-----
|
2
|
+
MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBALRiMLAh9iimur8V
|
3
|
+
A7qVvdqxevEuUkW4K+2KdMXmnQbG9Aa7k7eBjK1S+0LYmVjPKlJGNXHDGuy5Fw/d
|
4
|
+
7rjVJ0BLB+ubPK8iA/Tw3hLQgXMRRGRXXCn8ikfuQfjUS1uZSatdLB81mydBETlJ
|
5
|
+
hI6GH4twrbDJCR2Bwy/XWXgqgGRzAgMBAAECgYBYWVtleUzavkbrPjy0T5FMou8H
|
6
|
+
X9u2AC2ry8vD/l7cqedtwMPp9k7TubgNFo+NGvKsl2ynyprOZR1xjQ7WgrgVB+mm
|
7
|
+
uScOM/5HVceFuGRDhYTCObE+y1kxRloNYXnx3ei1zbeYLPCHdhxRYW7T0qcynNmw
|
8
|
+
rn05/KO2RLjgQNalsQJBANeA3Q4Nugqy4QBUCEC09SqylT2K9FrrItqL2QKc9v0Z
|
9
|
+
zO2uwllCbg0dwpVuYPYXYvikNHHg+aCWF+VXsb9rpPsCQQDWR9TT4ORdzoj+Nccn
|
10
|
+
qkMsDmzt0EfNaAOwHOmVJ2RVBspPcxt5iN4HI7HNeG6U5YsFBb+/GZbgfBT3kpNG
|
11
|
+
WPTpAkBI+gFhjfJvRw38n3g/+UeAkwMI2TJQS4n8+hid0uus3/zOjDySH3XHCUno
|
12
|
+
cn1xOJAyZODBo47E+67R4jV1/gzbAkEAklJaspRPXP877NssM5nAZMU0/O/NGCZ+
|
13
|
+
3jPgDUno6WbJn5cqm8MqWhW1xGkImgRk+fkDBquiq4gPiT898jusgQJAd5Zrr6Q8
|
14
|
+
AO/0isr/3aa6O6NLQxISLKcPDk2NOccAfS/xOtfOz4sJYM3+Bs4Io9+dZGSDCA54
|
15
|
+
Lw03eHTNQghS0A==
|
16
|
+
-----END PRIVATE KEY-----
|
data/test/simple_oauth_test.rb
CHANGED
@@ -1,21 +1,6 @@
|
|
1
1
|
require 'helper'
|
2
2
|
|
3
3
|
class SimpleOAuthTest < Test::Unit::TestCase
|
4
|
-
def test_initialize
|
5
|
-
header = SimpleOAuth::Header.new(:get, 'HTTPS://api.TWITTER.com:443/1/statuses/friendships.json#anchor', {})
|
6
|
-
|
7
|
-
# HTTP method should be an uppercase string.
|
8
|
-
#
|
9
|
-
# See: http://oauth.net/core/1.0/#rfc.section.9.1.3
|
10
|
-
assert_equal 'GET', header.method
|
11
|
-
|
12
|
-
# Request URL should downcase the scheme and authority parts as well as
|
13
|
-
# remove the query and fragment parts.
|
14
|
-
#
|
15
|
-
# See: http://oauth.net/core/1.0/#rfc.section.9.1.2
|
16
|
-
assert_equal 'https://api.twitter.com/1/statuses/friendships.json', header.url
|
17
|
-
end
|
18
|
-
|
19
4
|
def test_default_options
|
20
5
|
# Default header options should change with each call due to generation of
|
21
6
|
# a unique "timestamp" and "nonce" value combination.
|
@@ -33,24 +18,6 @@ class SimpleOAuthTest < Test::Unit::TestCase
|
|
33
18
|
assert_equal '1.0', default_options[:version]
|
34
19
|
end
|
35
20
|
|
36
|
-
def test_attributes
|
37
|
-
attribute_options = SimpleOAuth::Header::ATTRIBUTE_KEYS.inject({}){|o,a| o.merge(a => a.to_s.upcase) }
|
38
|
-
options = attribute_options.merge(:other => 'OTHER')
|
39
|
-
header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friendships.json', {}, options)
|
40
|
-
attributes = header.send(:attributes)
|
41
|
-
|
42
|
-
# OAuth header attributes are all to begin with the "oauth_" prefix.
|
43
|
-
assert attributes.all?{|k,v| k.to_s =~ /^oauth_/ }
|
44
|
-
|
45
|
-
# Custom options not included in the list of valid attribute keys should
|
46
|
-
# not be included in the header attributes.
|
47
|
-
assert !attributes.key?(:oauth_other)
|
48
|
-
|
49
|
-
# Valid attribute option values should be preserved.
|
50
|
-
assert_equal attribute_options.size, attributes.size
|
51
|
-
assert attributes.all?{|k,v| k.to_s == "oauth_#{v.downcase}" }
|
52
|
-
end
|
53
|
-
|
54
21
|
def test_encode
|
55
22
|
# Non-word characters should be URL encoded...
|
56
23
|
[' ', '!', '@', '#', '$', '%', '^', '&'].each do |character|
|
@@ -65,85 +32,122 @@ class SimpleOAuthTest < Test::Unit::TestCase
|
|
65
32
|
end
|
66
33
|
end
|
67
34
|
|
68
|
-
def
|
69
|
-
#
|
70
|
-
|
71
|
-
assert_equal [], header.send(:url_params)
|
35
|
+
def test_decode
|
36
|
+
# Pending
|
37
|
+
end
|
72
38
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
url_params = header.send(:url_params)
|
77
|
-
assert_kind_of Array, url_params
|
78
|
-
assert_equal [['test', 'TEST']], url_params
|
39
|
+
def test_parse
|
40
|
+
header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friends.json', {})
|
41
|
+
parsed_options = SimpleOAuth::Header.parse(header)
|
79
42
|
|
80
|
-
#
|
81
|
-
|
82
|
-
|
43
|
+
# Parsed options should be a Hash.
|
44
|
+
assert_kind_of Hash, parsed_options
|
45
|
+
|
46
|
+
# Parsed options should equal the options used to build the header, along
|
47
|
+
# with the additional signature.
|
48
|
+
assert_equal header.options, parsed_options.reject{|k,v| k == :signature }
|
83
49
|
end
|
84
50
|
|
85
|
-
def
|
86
|
-
header = SimpleOAuth::Header.new(:get, '
|
87
|
-
header.stubs(:attributes).returns(:attribute => 'ATTRIBUTE')
|
88
|
-
header.stubs(:params).returns('param' => 'PARAM')
|
89
|
-
header.stubs(:url_params).returns([['url_param', '1'], ['url_param', '2']])
|
51
|
+
def test_initialize
|
52
|
+
header = SimpleOAuth::Header.new(:get, 'HTTPS://api.TWITTER.com:443/1/statuses/friendships.json#anchor', {})
|
90
53
|
|
91
|
-
#
|
92
|
-
#
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
54
|
+
# HTTP method should be an uppercase string.
|
55
|
+
#
|
56
|
+
# See: http://oauth.net/core/1.0/#rfc.section.9.1.3
|
57
|
+
assert_equal 'GET', header.method
|
58
|
+
|
59
|
+
# Request URL should downcase the scheme and authority parts as well as
|
60
|
+
# remove the query and fragment parts.
|
61
|
+
#
|
62
|
+
# See: http://oauth.net/core/1.0/#rfc.section.9.1.2
|
63
|
+
assert_equal 'https://api.twitter.com/1/statuses/friendships.json', header.url
|
97
64
|
end
|
98
65
|
|
99
|
-
def
|
100
|
-
|
101
|
-
|
66
|
+
def test_url
|
67
|
+
# Pending
|
68
|
+
end
|
102
69
|
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
parts = normalized_params.split('&')
|
108
|
-
pairs = parts.map{|p| p.split('=') }
|
109
|
-
assert_kind_of String, normalized_params
|
110
|
-
assert_equal signature_params.size, parts.size
|
111
|
-
assert pairs.all?{|p| p.size == 2 }
|
70
|
+
def test_to_s
|
71
|
+
header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friends.json', {})
|
72
|
+
assert_equal "OAuth #{header.send(:normalized_attributes)}", header.to_s
|
73
|
+
end
|
112
74
|
|
113
|
-
|
114
|
-
#
|
115
|
-
|
75
|
+
def test_valid?
|
76
|
+
# When given consumer and token secrets, those secrets must be passed into
|
77
|
+
# the parsed header validation in order for the validity check to pass.
|
78
|
+
secrets = {:consumer_secret => 'CONSUMER_SECRET', :token_secret => 'TOKEN_SECRET'}
|
79
|
+
header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friends.json', {}, secrets)
|
80
|
+
parsed_header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friends.json', {}, header)
|
81
|
+
assert !parsed_header.valid?
|
82
|
+
assert parsed_header.valid?(secrets)
|
83
|
+
|
84
|
+
# Using the RSA-SHA1 signature method, the consumer secret must be a valid
|
85
|
+
# RSA private key. When parsing the header on the server side, the same
|
86
|
+
# consumer secret must be included in order for the header to validate.
|
87
|
+
secrets = {:consumer_secret => File.read('test/rsa_private_key')}
|
88
|
+
header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friends.json', {}, secrets.merge(:signature_method => 'RSA-SHA1'))
|
89
|
+
parsed_header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friends.json', {}, header)
|
90
|
+
assert_raise(TypeError){ parsed_header.valid? }
|
91
|
+
assert parsed_header.valid?(secrets)
|
92
|
+
|
93
|
+
# Like the default HMAC-RSA1 signature method, the PLAINTEXT method
|
94
|
+
# requires use of both a consumer secret and a token secret. A parsed
|
95
|
+
# header will not validate without these secret values.
|
96
|
+
secrets = {:consumer_secret => 'CONSUMER_SECRET', :token_secret => 'TOKEN_SECRET'}
|
97
|
+
header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friends.json', {}, secrets.merge(:signature_method => 'PLAINTEXT'))
|
98
|
+
parsed_header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friends.json', {}, header)
|
99
|
+
assert !parsed_header.valid?
|
100
|
+
assert parsed_header.valid?(secrets)
|
116
101
|
end
|
117
102
|
|
118
|
-
def
|
119
|
-
header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/
|
120
|
-
header.stubs(:
|
121
|
-
header.stubs(:url).returns('URL')
|
122
|
-
header.stubs(:normalized_params).returns('NORMALIZED_PARAMS')
|
103
|
+
def test_normalized_attributes
|
104
|
+
header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friends.json', {})
|
105
|
+
header.stubs(:signed_attributes).returns(:d => 1, :c => 2, :b => 3, :a => 4)
|
123
106
|
|
124
|
-
# Should
|
125
|
-
#
|
126
|
-
assert_equal '
|
107
|
+
# Should return the OAuth header attributes, sorted by name, with quoted
|
108
|
+
# values and comma-separated.
|
109
|
+
assert_equal 'a="4", b="3", c="2", d="1"', header.send(:normalized_attributes)
|
127
110
|
|
128
|
-
|
129
|
-
header.stubs(:
|
130
|
-
header.
|
111
|
+
# Values should also be URL encoded.
|
112
|
+
header.stubs(:signed_attributes).returns(1 => '!', 2 => '@', 3 => '#', 4 => '$')
|
113
|
+
assert_equal '1="%21", 2="%40", 3="%23", 4="%24"', header.send(:normalized_attributes)
|
114
|
+
end
|
131
115
|
|
132
|
-
|
133
|
-
|
116
|
+
def test_signed_attributes
|
117
|
+
header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friends.json', {})
|
118
|
+
assert header.send(:signed_attributes).keys.include?(:oauth_signature)
|
134
119
|
end
|
135
120
|
|
136
|
-
def
|
137
|
-
|
138
|
-
|
121
|
+
def test_attributes
|
122
|
+
attribute_options = SimpleOAuth::Header::ATTRIBUTE_KEYS.inject({}){|o,a| o.merge(a => a.to_s.upcase) }
|
123
|
+
options = attribute_options.merge(:other => 'OTHER')
|
124
|
+
header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friendships.json', {}, options)
|
125
|
+
attributes = header.send(:attributes)
|
139
126
|
|
140
|
-
#
|
141
|
-
|
127
|
+
# OAuth header attributes are all to begin with the "oauth_" prefix.
|
128
|
+
assert attributes.all?{|k,v| k.to_s =~ /^oauth_/ }
|
142
129
|
|
143
|
-
|
130
|
+
# Custom options not included in the list of valid attribute keys should
|
131
|
+
# not be included in the header attributes.
|
132
|
+
assert !attributes.key?(:oauth_other)
|
144
133
|
|
145
|
-
#
|
146
|
-
assert_equal
|
134
|
+
# Valid attribute option values should be preserved.
|
135
|
+
assert_equal attribute_options.size, attributes.size
|
136
|
+
assert attributes.all?{|k,v| k.to_s == "oauth_#{v.downcase}" }
|
137
|
+
end
|
138
|
+
|
139
|
+
def test_signature
|
140
|
+
header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friends.json', {}, :signature_method => 'HMAC-SHA1')
|
141
|
+
header.expects(:hmac_sha1_signature).once.returns('HMAC_SHA1_SIGNATURE')
|
142
|
+
assert_equal 'HMAC_SHA1_SIGNATURE', header.send(:signature)
|
143
|
+
|
144
|
+
header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friends.json', {}, :signature_method => 'RSA-SHA1')
|
145
|
+
header.expects(:rsa_sha1_signature).once.returns('RSA_SHA1_SIGNATURE')
|
146
|
+
assert_equal 'RSA_SHA1_SIGNATURE', header.send(:signature)
|
147
|
+
|
148
|
+
header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friends.json', {}, :signature_method => 'PLAINTEXT')
|
149
|
+
header.expects(:plaintext_signature).once.returns('PLAINTEXT_SIGNATURE')
|
150
|
+
assert_equal 'PLAINTEXT_SIGNATURE', header.send(:signature)
|
147
151
|
end
|
148
152
|
|
149
153
|
def test_hmac_sha1_signature
|
@@ -172,89 +176,120 @@ class SimpleOAuthTest < Test::Unit::TestCase
|
|
172
176
|
assert_equal successful, header.to_s
|
173
177
|
end
|
174
178
|
|
175
|
-
def
|
176
|
-
header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/
|
177
|
-
header.
|
178
|
-
assert_equal 'HMAC_SHA1_SIGNATURE', header.send(:signature)
|
179
|
+
def test_secret
|
180
|
+
header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friendships.json', {})
|
181
|
+
header.stubs(:options).returns(:consumer_secret => 'CONSUMER_SECRET', :token_secret => 'TOKEN_SECRET')
|
179
182
|
|
180
|
-
|
181
|
-
header.
|
182
|
-
assert_equal 'RSA_SHA1_SIGNATURE', header.send(:signature)
|
183
|
+
# Should combine the consumer and token secrets with an ampersand.
|
184
|
+
assert_equal 'CONSUMER_SECRET&TOKEN_SECRET', header.send(:secret)
|
183
185
|
|
184
|
-
header
|
185
|
-
|
186
|
-
|
186
|
+
header.stubs(:options).returns(:consumer_secret => 'CONSUM#R_SECRET', :token_secret => 'TOKEN_S#CRET')
|
187
|
+
|
188
|
+
# Should URL encode each secret value before combination.
|
189
|
+
assert_equal 'CONSUM%23R_SECRET&TOKEN_S%23CRET', header.send(:secret)
|
187
190
|
end
|
188
191
|
|
189
|
-
def
|
190
|
-
header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/
|
191
|
-
|
192
|
+
def test_signature_base
|
193
|
+
header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friendships.json', {})
|
194
|
+
header.stubs(:method).returns('METHOD')
|
195
|
+
header.stubs(:url).returns('URL')
|
196
|
+
header.stubs(:normalized_params).returns('NORMALIZED_PARAMS')
|
197
|
+
|
198
|
+
# Should combine HTTP method, URL and normalized parameters string using
|
199
|
+
# ampersands.
|
200
|
+
assert_equal 'METHOD&URL&NORMALIZED_PARAMS', header.send(:signature_base)
|
201
|
+
|
202
|
+
header.stubs(:method).returns('ME#HOD')
|
203
|
+
header.stubs(:url).returns('U#L')
|
204
|
+
header.stubs(:normalized_params).returns('NORMAL#ZED_PARAMS')
|
205
|
+
|
206
|
+
# Each of the three combined values should be URL encoded.
|
207
|
+
assert_equal 'ME%23HOD&U%23L&NORMAL%23ZED_PARAMS', header.send(:signature_base)
|
192
208
|
end
|
193
209
|
|
194
|
-
def
|
195
|
-
header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/
|
196
|
-
header.stubs(:
|
210
|
+
def test_normalized_params
|
211
|
+
header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friendships.json', {})
|
212
|
+
header.stubs(:signature_params).returns([['A', '4'], ['B', '3'], ['B', '2'], ['C', '1'], ['D[]', '0 ']])
|
197
213
|
|
198
|
-
#
|
199
|
-
#
|
200
|
-
|
214
|
+
# The +normalized_params+ string should join key=value pairs with
|
215
|
+
# ampersands.
|
216
|
+
signature_params = header.send(:signature_params)
|
217
|
+
normalized_params = header.send(:normalized_params)
|
218
|
+
parts = normalized_params.split('&')
|
219
|
+
pairs = parts.map{|p| p.split('=') }
|
220
|
+
assert_kind_of String, normalized_params
|
221
|
+
assert_equal signature_params.size, parts.size
|
222
|
+
assert pairs.all?{|p| p.size == 2 }
|
201
223
|
|
202
|
-
#
|
203
|
-
|
204
|
-
assert_equal
|
224
|
+
# The signature parameters should be sorted and the keys/values URL encoded
|
225
|
+
# first.
|
226
|
+
assert_equal signature_params.sort_by(&:to_s), pairs.map{|k,v| [URI.decode(k), URI.decode(v)] }
|
205
227
|
end
|
206
228
|
|
207
|
-
def
|
208
|
-
header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/
|
209
|
-
|
229
|
+
def test_signature_params
|
230
|
+
header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friendships.json', {})
|
231
|
+
header.stubs(:attributes).returns(:attribute => 'ATTRIBUTE')
|
232
|
+
header.stubs(:params).returns('param' => 'PARAM')
|
233
|
+
header.stubs(:url_params).returns([['url_param', '1'], ['url_param', '2']])
|
234
|
+
|
235
|
+
# Should combine OAuth header attributes, body parameters and URL
|
236
|
+
# parameters into an array of key value pairs.
|
237
|
+
signature_params = header.send(:signature_params)
|
238
|
+
assert_kind_of Array, signature_params
|
239
|
+
assert_equal [:attribute, 'param', 'url_param', 'url_param'], signature_params.map(&:first)
|
240
|
+
assert_equal ['ATTRIBUTE', 'PARAM', '1', '2'], signature_params.map(&:last)
|
210
241
|
end
|
211
242
|
|
212
|
-
def
|
213
|
-
|
214
|
-
|
243
|
+
def test_url_params
|
244
|
+
# A URL with no query parameters should produce empty +url_params+
|
245
|
+
header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friendships.json', {})
|
246
|
+
assert_equal [], header.send(:url_params)
|
215
247
|
|
216
|
-
#
|
217
|
-
|
248
|
+
# A URL with query parameters should return a hash having array values
|
249
|
+
# containing the given query parameters.
|
250
|
+
header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friendships.json?test=TEST', {})
|
251
|
+
url_params = header.send(:url_params)
|
252
|
+
assert_kind_of Array, url_params
|
253
|
+
assert_equal [['test', 'TEST']], url_params
|
218
254
|
|
219
|
-
#
|
220
|
-
|
221
|
-
assert_equal
|
255
|
+
# If a query parameter is repeated, the values should be sorted.
|
256
|
+
header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friendships.json?test=1&test=2', {})
|
257
|
+
assert_equal [['test', '1'], ['test', '2']], header.send(:url_params)
|
222
258
|
end
|
223
259
|
|
224
|
-
def
|
225
|
-
#
|
226
|
-
#
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
parsed_header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friends.json', {}, header)
|
239
|
-
assert !parsed_header.valid?
|
240
|
-
assert parsed_header.valid?(secrets)
|
260
|
+
def test_rsa_sha1_signature
|
261
|
+
# Sample request taken from:
|
262
|
+
# http://wiki.oauth.net/TestCases
|
263
|
+
options = {
|
264
|
+
:consumer_key => 'dpf43f3p2l4k3l03',
|
265
|
+
:consumer_secret => File.read('test/rsa_private_key'),
|
266
|
+
:nonce => '13917289812797014437',
|
267
|
+
:signature_method => 'RSA-SHA1',
|
268
|
+
:timestamp => '1196666512'
|
269
|
+
}
|
270
|
+
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"'
|
271
|
+
header = SimpleOAuth::Header.new(:get, 'http://photos.example.net/photos', {:file => 'vacaction.jpg', :size => 'original'}, options)
|
272
|
+
assert_equal successful, header.to_s
|
273
|
+
end
|
241
274
|
|
242
|
-
|
243
|
-
#
|
244
|
-
|
245
|
-
secrets = {:private_key => 'PRIVATE_KEY'}
|
246
|
-
header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friends.json', {}, secrets.merge(:signature_method => 'RSA-SHA1'))
|
247
|
-
parsed_header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friends.json', {}, header)
|
248
|
-
assert !parsed_header.valid?
|
249
|
-
assert parsed_header.valid?(secrets)
|
275
|
+
def test_private_key
|
276
|
+
# Pending
|
277
|
+
end
|
250
278
|
|
251
|
-
|
252
|
-
#
|
253
|
-
#
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
279
|
+
def plaintext_signature
|
280
|
+
# Sample request taken from:
|
281
|
+
# http://oauth.googlecode.com/svn/code/javascript/example/signature.html
|
282
|
+
options = {
|
283
|
+
:consumer_key => 'abcd',
|
284
|
+
:consumer_secret => 'efgh',
|
285
|
+
:nonce => 'oLKtec51GQy',
|
286
|
+
:signature_method => 'PLAINTEXT',
|
287
|
+
:timestamp => '1286977095',
|
288
|
+
:token => 'ijkl',
|
289
|
+
:token_secret => 'mnop'
|
290
|
+
}
|
291
|
+
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"'
|
292
|
+
header = SimpleOAuth::Header.new(:get, 'http://host.net/resource?name=value', {:name => 'value'}, options)
|
293
|
+
assert_equal successful, header.to_s
|
259
294
|
end
|
260
295
|
end
|
metadata
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: simple_oauth
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
+
hash: 25
|
4
5
|
prerelease: false
|
5
6
|
segments:
|
6
7
|
- 0
|
7
8
|
- 1
|
8
|
-
-
|
9
|
-
version: 0.1.
|
9
|
+
- 1
|
10
|
+
version: 0.1.1
|
10
11
|
platform: ruby
|
11
12
|
authors:
|
12
13
|
- Steve Richert
|
@@ -14,7 +15,7 @@ autorequire:
|
|
14
15
|
bindir: bin
|
15
16
|
cert_chain: []
|
16
17
|
|
17
|
-
date: 2010-10-
|
18
|
+
date: 2010-10-13 00:00:00 -04:00
|
18
19
|
default_executable:
|
19
20
|
dependencies:
|
20
21
|
- !ruby/object:Gem::Dependency
|
@@ -25,6 +26,7 @@ dependencies:
|
|
25
26
|
requirements:
|
26
27
|
- - ">="
|
27
28
|
- !ruby/object:Gem::Version
|
29
|
+
hash: 3
|
28
30
|
segments:
|
29
31
|
- 0
|
30
32
|
version: "0"
|
@@ -41,12 +43,16 @@ extra_rdoc_files:
|
|
41
43
|
- README.rdoc
|
42
44
|
files:
|
43
45
|
- .gitignore
|
46
|
+
- Gemfile
|
47
|
+
- Gemfile.lock
|
44
48
|
- LICENSE
|
45
49
|
- README.rdoc
|
46
50
|
- Rakefile
|
47
51
|
- init.rb
|
48
52
|
- lib/simple_oauth.rb
|
53
|
+
- simple_oauth.gemspec
|
49
54
|
- test/helper.rb
|
55
|
+
- test/rsa_private_key
|
50
56
|
- test/simple_oauth_test.rb
|
51
57
|
has_rdoc: true
|
52
58
|
homepage: http://github.com/laserlemon/simple_oauth
|
@@ -62,6 +68,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
62
68
|
requirements:
|
63
69
|
- - ">="
|
64
70
|
- !ruby/object:Gem::Version
|
71
|
+
hash: 3
|
65
72
|
segments:
|
66
73
|
- 0
|
67
74
|
version: "0"
|
@@ -70,6 +77,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
70
77
|
requirements:
|
71
78
|
- - ">="
|
72
79
|
- !ruby/object:Gem::Version
|
80
|
+
hash: 3
|
73
81
|
segments:
|
74
82
|
- 0
|
75
83
|
version: "0"
|