simple_oauth 0.1.9 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.rspec +3 -0
- data/CONTRIBUTING.md +8 -0
- data/README.md +0 -9
- data/lib/simple_oauth/header.rb +31 -21
- data/simple_oauth.gemspec +15 -14
- data/spec/helper.rb +21 -0
- data/spec/simple_oauth/header_spec.rb +122 -122
- metadata +28 -25
- data/spec/spec_helper.rb +0 -10
data/.rspec
ADDED
data/CONTRIBUTING.md
ADDED
@@ -0,0 +1,8 @@
|
|
1
|
+
## Contributing
|
2
|
+
1. Fork the project.
|
3
|
+
2. Create a topic branch.
|
4
|
+
3. Add failing tests.
|
5
|
+
4. Add code to pass the failing tests.
|
6
|
+
5. Run `bundle exec rake`. If failing, repeat step 4.
|
7
|
+
6. Commit and push your changes.
|
8
|
+
7. Submit a pull request. Please do not include changes to the gemspec.
|
data/README.md
CHANGED
@@ -2,15 +2,6 @@
|
|
2
2
|
|
3
3
|
Simply builds and verifies OAuth headers
|
4
4
|
|
5
|
-
## Contributing
|
6
|
-
1. Fork the project.
|
7
|
-
2. Create a topic branch.
|
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.
|
13
|
-
|
14
5
|
## Supported Rubies
|
15
6
|
This library aims to support and is [tested
|
16
7
|
against](http://travis-ci.org/laserlemon/simple_oauth) the following Ruby
|
data/lib/simple_oauth/header.rb
CHANGED
@@ -6,32 +6,42 @@ require 'cgi'
|
|
6
6
|
module SimpleOAuth
|
7
7
|
class Header
|
8
8
|
ATTRIBUTE_KEYS = [:callback, :consumer_key, :nonce, :signature_method, :timestamp, :token, :verifier, :version] unless defined? ::SimpleOAuth::Header::ATTRIBUTE_KEYS
|
9
|
+
attr_reader :method, :params, :options
|
9
10
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
11
|
+
class << self
|
12
|
+
def default_options
|
13
|
+
{
|
14
|
+
:nonce => OpenSSL::Random.random_bytes(16).unpack('H*')[0],
|
15
|
+
:signature_method => 'HMAC-SHA1',
|
16
|
+
:timestamp => Time.now.to_i.to_s,
|
17
|
+
:version => '1.0'
|
18
|
+
}
|
19
|
+
end
|
18
20
|
|
19
|
-
|
20
|
-
|
21
|
-
|
21
|
+
def parse(header)
|
22
|
+
header.to_s.sub(/^OAuth\s/, '').split(/,\s*/).inject({}) do |attributes, pair|
|
23
|
+
match = pair.match(/^(\w+)\=\"([^\"]*)\"$/)
|
24
|
+
attributes.merge(match[1].sub(/^oauth_/, '').to_sym => decode(match[2]))
|
25
|
+
end
|
26
|
+
end
|
22
27
|
|
23
|
-
|
24
|
-
|
25
|
-
|
28
|
+
def escape(value)
|
29
|
+
uri_parser.escape(value.to_s, /[^a-z0-9\-\.\_\~]/i)
|
30
|
+
end
|
31
|
+
alias encode escape
|
26
32
|
|
27
|
-
|
28
|
-
|
29
|
-
match = pair.match(/^(\w+)\=\"([^\"]*)\"$/)
|
30
|
-
attributes.merge(match[1].sub(/^oauth_/, '').to_sym => decode(match[2]))
|
33
|
+
def unescape(value)
|
34
|
+
uri_parser.unescape(value.to_s)
|
31
35
|
end
|
32
|
-
|
36
|
+
alias decode unescape
|
33
37
|
|
34
|
-
|
38
|
+
private
|
39
|
+
|
40
|
+
def uri_parser
|
41
|
+
@uri_parser ||= URI.const_defined?(:Parser) ? URI::Parser.new : URI
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
35
45
|
|
36
46
|
def initialize(method, url, params, oauth = {})
|
37
47
|
@method = method.to_s.upcase
|
@@ -65,7 +75,7 @@ module SimpleOAuth
|
|
65
75
|
attributes.merge(:oauth_signature => signature)
|
66
76
|
end
|
67
77
|
|
68
|
-
|
78
|
+
private
|
69
79
|
|
70
80
|
def normalized_attributes
|
71
81
|
signed_attributes.sort_by{|k,v| k.to_s }.map{|k,v| %(#{k}="#{self.class.encode(v)}") }.join(', ')
|
data/simple_oauth.gemspec
CHANGED
@@ -1,20 +1,21 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
-
Gem::Specification.new do |
|
4
|
-
|
5
|
-
|
3
|
+
Gem::Specification.new do |spec|
|
4
|
+
spec.name = 'simple_oauth'
|
5
|
+
spec.version = '0.2.0'
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
7
|
+
spec.authors = ["Steve Richert", "Erik Michaels-Ober"]
|
8
|
+
spec.email = ['steve.richert@gmail.com', 'sferik@gmail.com']
|
9
|
+
spec.description = 'Simply builds and verifies OAuth headers'
|
10
|
+
spec.summary = spec.description
|
11
|
+
spec.homepage = 'https://github.com/laserlemon/simple_oauth'
|
12
|
+
spec.licenses = ['MIT']
|
12
13
|
|
13
|
-
|
14
|
-
|
15
|
-
|
14
|
+
spec.add_development_dependency 'rake'
|
15
|
+
spec.add_development_dependency 'rspec', '>= 2'
|
16
|
+
spec.add_development_dependency 'simplecov'
|
16
17
|
|
17
|
-
|
18
|
-
|
19
|
-
|
18
|
+
spec.files = `git ls-files`.split($\)
|
19
|
+
spec.test_files = spec.files.grep(/^test\//)
|
20
|
+
spec.require_paths = ["lib"]
|
20
21
|
end
|
data/spec/helper.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
unless ENV['CI']
|
2
|
+
require 'simplecov'
|
3
|
+
SimpleCov.start do
|
4
|
+
add_filter 'spec'
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
require 'simple_oauth'
|
9
|
+
require 'rspec'
|
10
|
+
|
11
|
+
def uri_parser
|
12
|
+
@uri_parser ||= URI.const_defined?(:Parser) ? URI::Parser.new : URI
|
13
|
+
end
|
14
|
+
|
15
|
+
RSpec.configure do |config|
|
16
|
+
config.expect_with :rspec do |c|
|
17
|
+
c.syntax = :expect
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
Dir[File.expand_path('../support/**/*.rb', __FILE__)].each{|f| require f }
|
@@ -1,50 +1,50 @@
|
|
1
|
-
#
|
1
|
+
# encoding: utf-8
|
2
2
|
|
3
|
-
require '
|
3
|
+
require 'helper'
|
4
4
|
|
5
5
|
describe SimpleOAuth::Header do
|
6
|
-
describe
|
6
|
+
describe ".default_options" do
|
7
7
|
let(:default_options){ SimpleOAuth::Header.default_options }
|
8
8
|
|
9
|
-
it
|
10
|
-
SimpleOAuth::Header.default_options.
|
9
|
+
it "is different every time" do
|
10
|
+
expect(SimpleOAuth::Header.default_options).not_to eq default_options
|
11
11
|
end
|
12
12
|
|
13
|
-
it
|
13
|
+
it "is used for new headers" do
|
14
14
|
SimpleOAuth::Header.stub(:default_options => default_options)
|
15
15
|
header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friendships.json', {})
|
16
|
-
header.options.
|
16
|
+
expect(header.options).to eq default_options
|
17
17
|
end
|
18
18
|
|
19
|
-
it
|
20
|
-
default_options[:signature_method].
|
21
|
-
default_options[:version].
|
19
|
+
it "includes a signature method and an OAuth version" do
|
20
|
+
expect(default_options[:signature_method]).not_to be_nil
|
21
|
+
expect(default_options[:version]).not_to be_nil
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
25
|
-
describe
|
26
|
-
it
|
25
|
+
describe ".escape" do
|
26
|
+
it "escapes (most) non-word characters" do
|
27
27
|
[' ', '!', '@', '#', '$', '%', '^', '&'].each do |character|
|
28
|
-
|
29
|
-
|
30
|
-
|
28
|
+
escaped = SimpleOAuth::Header.escape(character)
|
29
|
+
expect(escaped).not_to eq character
|
30
|
+
expect(escaped).to eq uri_parser.escape(character, /.*/)
|
31
31
|
end
|
32
32
|
end
|
33
33
|
|
34
|
-
it
|
34
|
+
it "does not escape - . or ~" do
|
35
35
|
['-', '.', '~'].each do |character|
|
36
|
-
|
37
|
-
|
36
|
+
escaped = SimpleOAuth::Header.escape(character)
|
37
|
+
expect(escaped).to eq character
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
41
41
|
def self.test_special_characters
|
42
|
-
it
|
43
|
-
SimpleOAuth::Header.
|
42
|
+
it "escapes non-ASCII characters" do
|
43
|
+
expect(SimpleOAuth::Header.escape('é')).to eq '%C3%A9'
|
44
44
|
end
|
45
45
|
|
46
|
-
it
|
47
|
-
SimpleOAuth::Header.
|
46
|
+
it "escapes multibyte characters" do
|
47
|
+
expect(SimpleOAuth::Header.escape('あ')).to eq '%E3%81%82'
|
48
48
|
end
|
49
49
|
end
|
50
50
|
|
@@ -65,118 +65,118 @@ describe SimpleOAuth::Header do
|
|
65
65
|
end
|
66
66
|
end
|
67
67
|
|
68
|
-
describe
|
68
|
+
describe ".unescape" do
|
69
69
|
pending
|
70
70
|
end
|
71
71
|
|
72
|
-
describe
|
72
|
+
describe ".parse" do
|
73
73
|
let(:header){ SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friends.json', {}) }
|
74
74
|
let(:parsed_options){ SimpleOAuth::Header.parse(header) }
|
75
75
|
|
76
|
-
it
|
77
|
-
parsed_options.
|
76
|
+
it "returns a hash" do
|
77
|
+
expect(parsed_options).to be_a(Hash)
|
78
78
|
end
|
79
79
|
|
80
|
-
it
|
81
|
-
parsed_options.reject{|k,_| k == :signature }.
|
80
|
+
it "includes the options used to build the header" do
|
81
|
+
expect(parsed_options.reject{|k,_| k == :signature }).to eq header.options
|
82
82
|
end
|
83
83
|
|
84
|
-
it
|
85
|
-
header.options.
|
86
|
-
parsed_options.
|
87
|
-
parsed_options[:signature].
|
84
|
+
it "includes a signature" do
|
85
|
+
expect(header.options).not_to have_key(:signature)
|
86
|
+
expect(parsed_options).to have_key(:signature)
|
87
|
+
expect(parsed_options[:signature]).not_to be_nil
|
88
88
|
end
|
89
89
|
|
90
|
-
it
|
90
|
+
it "handles optional 'linear white space'" do
|
91
91
|
parsed_header_with_spaces = SimpleOAuth::Header.parse '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"'
|
92
|
-
parsed_header_with_spaces.
|
93
|
-
parsed_header_with_spaces.keys.size.
|
92
|
+
expect(parsed_header_with_spaces).to be_a_kind_of(Hash)
|
93
|
+
expect(parsed_header_with_spaces.keys.size).to eq 7
|
94
94
|
|
95
95
|
parsed_header_with_tabs = SimpleOAuth::Header.parse '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"'
|
96
|
-
parsed_header_with_tabs.
|
97
|
-
parsed_header_with_tabs.keys.size.
|
96
|
+
expect(parsed_header_with_tabs).to be_a_kind_of(Hash)
|
97
|
+
expect(parsed_header_with_tabs.keys.size).to eq 7
|
98
98
|
|
99
99
|
parsed_header_with_spaces_and_tabs = SimpleOAuth::Header.parse '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"'
|
100
|
-
parsed_header_with_spaces_and_tabs.
|
101
|
-
parsed_header_with_spaces_and_tabs.keys.size.
|
100
|
+
expect(parsed_header_with_spaces_and_tabs).to be_a_kind_of(Hash)
|
101
|
+
expect(parsed_header_with_spaces_and_tabs.keys.size).to eq 7
|
102
102
|
|
103
103
|
parsed_header_without_spaces = SimpleOAuth::Header.parse '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"'
|
104
|
-
parsed_header_without_spaces.
|
105
|
-
parsed_header_without_spaces.keys.size.
|
104
|
+
expect(parsed_header_without_spaces).to be_a_kind_of(Hash)
|
105
|
+
expect(parsed_header_without_spaces.keys.size).to eq 7
|
106
106
|
end
|
107
107
|
end
|
108
108
|
|
109
|
-
describe
|
109
|
+
describe "#initialize" do
|
110
110
|
let(:header){ SimpleOAuth::Header.new(:get, 'HTTPS://api.TWITTER.com:443/1/statuses/friendships.json?foo=bar#anchor', {}) }
|
111
111
|
|
112
|
-
it
|
113
|
-
header.method.
|
112
|
+
it "stringifies and uppercases the request method" do
|
113
|
+
expect(header.method).to eq 'GET'
|
114
114
|
end
|
115
115
|
|
116
|
-
it
|
117
|
-
header.url.
|
116
|
+
it "downcases the scheme and authority" do
|
117
|
+
expect(header.url).to match %r(^https://api\.twitter\.com/)
|
118
118
|
end
|
119
119
|
|
120
|
-
it
|
121
|
-
header.url.
|
120
|
+
it "ignores the query and fragment" do
|
121
|
+
expect(header.url).to match %r(/1/statuses/friendships\.json$)
|
122
122
|
end
|
123
123
|
end
|
124
124
|
|
125
|
-
describe
|
126
|
-
context
|
127
|
-
it
|
125
|
+
describe "#valid?" do
|
126
|
+
context "using the HMAC-SHA1 signature method" do
|
127
|
+
it "requires consumer and token secrets" do
|
128
128
|
secrets = {:consumer_secret => 'CONSUMER_SECRET', :token_secret => 'TOKEN_SECRET'}
|
129
129
|
header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friends.json', {}, secrets)
|
130
130
|
parsed_header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friends.json', {}, header)
|
131
|
-
parsed_header.
|
132
|
-
parsed_header.
|
131
|
+
expect(parsed_header).not_to be_valid
|
132
|
+
expect(parsed_header).to be_valid(secrets)
|
133
133
|
end
|
134
134
|
end
|
135
135
|
|
136
|
-
context
|
137
|
-
it
|
136
|
+
context "using the RSA-SHA1 signature method" do
|
137
|
+
it "requires an identical private key" do
|
138
138
|
secrets = {:consumer_secret => rsa_private_key}
|
139
139
|
header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friends.json', {}, secrets.merge(:signature_method => 'RSA-SHA1'))
|
140
140
|
parsed_header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friends.json', {}, header)
|
141
141
|
expect{ parsed_header.valid? }.to raise_error(TypeError)
|
142
|
-
parsed_header.
|
142
|
+
expect(parsed_header).to be_valid(secrets)
|
143
143
|
end
|
144
144
|
end
|
145
145
|
|
146
|
-
context
|
147
|
-
it
|
146
|
+
context "using the RSA-SHA1 signature method" do
|
147
|
+
it "requires consumer and token secrets" do
|
148
148
|
secrets = {:consumer_secret => 'CONSUMER_SECRET', :token_secret => 'TOKEN_SECRET'}
|
149
149
|
header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friends.json', {}, secrets.merge(:signature_method => 'PLAINTEXT'))
|
150
150
|
parsed_header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friends.json', {}, header)
|
151
|
-
parsed_header.
|
152
|
-
parsed_header.
|
151
|
+
expect(parsed_header).not_to be_valid
|
152
|
+
expect(parsed_header).to be_valid(secrets)
|
153
153
|
end
|
154
154
|
end
|
155
155
|
end
|
156
156
|
|
157
|
-
describe
|
157
|
+
describe "#normalized_attributes" do
|
158
158
|
let(:header){ SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friends.json', {}) }
|
159
159
|
let(:normalized_attributes){ header.send(:normalized_attributes) }
|
160
160
|
|
161
|
-
it
|
161
|
+
it "returns a sorted-key, quoted-value and comma-separated list" do
|
162
162
|
header.stub(:signed_attributes => {:d => 1, :c => 2, :b => 3, :a => 4})
|
163
|
-
normalized_attributes.
|
163
|
+
expect(normalized_attributes).to eq 'a="4", b="3", c="2", d="1"'
|
164
164
|
end
|
165
165
|
|
166
|
-
it
|
166
|
+
it "URI encodes its values" do
|
167
167
|
header.stub(:signed_attributes => {1 => '!', 2 => '@', 3 => '#', 4 => '$'})
|
168
|
-
normalized_attributes.
|
168
|
+
expect(normalized_attributes).to eq '1="%21", 2="%40", 3="%23", 4="%24"'
|
169
169
|
end
|
170
170
|
end
|
171
171
|
|
172
|
-
describe
|
173
|
-
it
|
172
|
+
describe "#signed_attributes" do
|
173
|
+
it "includes the OAuth signature" do
|
174
174
|
header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friends.json', {})
|
175
|
-
header.send(:signed_attributes).
|
175
|
+
expect(header.send(:signed_attributes)).to have_key(:oauth_signature)
|
176
176
|
end
|
177
177
|
end
|
178
178
|
|
179
|
-
describe
|
179
|
+
describe "#attributes" do
|
180
180
|
let(:header) do
|
181
181
|
options = {}
|
182
182
|
SimpleOAuth::Header::ATTRIBUTE_KEYS.each{|k| options[k] = k.to_s.upcase }
|
@@ -185,45 +185,45 @@ describe SimpleOAuth::Header do
|
|
185
185
|
end
|
186
186
|
let(:attributes){ header.send(:attributes) }
|
187
187
|
|
188
|
-
it
|
189
|
-
attributes.keys.
|
188
|
+
it "prepends keys with 'oauth_'" do
|
189
|
+
expect(attributes.keys).to be_all{|k| k.to_s =~ /^oauth_/ }
|
190
190
|
end
|
191
191
|
|
192
|
-
it
|
193
|
-
attributes.keys.
|
194
|
-
attributes.
|
192
|
+
it "excludes keys not included in the list of valid attributes" do
|
193
|
+
expect(attributes.keys).to be_all{|k| k.is_a?(Symbol) }
|
194
|
+
expect(attributes).not_to have_key(:oauth_other)
|
195
195
|
end
|
196
196
|
|
197
|
-
it
|
198
|
-
attributes.size.
|
199
|
-
attributes.
|
197
|
+
it "preserves values for valid keys" do
|
198
|
+
expect(attributes.size).to eq SimpleOAuth::Header::ATTRIBUTE_KEYS.size
|
199
|
+
expect(attributes).to be_all{|k,v| k.to_s == "oauth_#{v.downcase}" }
|
200
200
|
end
|
201
201
|
end
|
202
202
|
|
203
|
-
describe
|
204
|
-
context
|
205
|
-
specify
|
203
|
+
describe "#signature" do
|
204
|
+
context "calls the appropriate signature method" do
|
205
|
+
specify "when using HMAC-SHA1" do
|
206
206
|
header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friends.json', {}, :signature_method => 'HMAC-SHA1')
|
207
207
|
header.should_receive(:hmac_sha1_signature).once.and_return('HMAC_SHA1_SIGNATURE')
|
208
|
-
header.send(:signature).
|
208
|
+
expect(header.send(:signature)).to eq 'HMAC_SHA1_SIGNATURE'
|
209
209
|
end
|
210
210
|
|
211
|
-
specify
|
211
|
+
specify "when using RSA-SHA1" do
|
212
212
|
header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friends.json', {}, :signature_method => 'RSA-SHA1')
|
213
213
|
header.should_receive(:rsa_sha1_signature).once.and_return('RSA_SHA1_SIGNATURE')
|
214
|
-
header.send(:signature).
|
214
|
+
expect(header.send(:signature)).to eq 'RSA_SHA1_SIGNATURE'
|
215
215
|
end
|
216
216
|
|
217
|
-
specify
|
217
|
+
specify "when using PLAINTEXT" do
|
218
218
|
header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friends.json', {}, :signature_method => 'PLAINTEXT')
|
219
219
|
header.should_receive(:plaintext_signature).once.and_return('PLAINTEXT_SIGNATURE')
|
220
|
-
header.send(:signature).
|
220
|
+
expect(header.send(:signature)).to eq 'PLAINTEXT_SIGNATURE'
|
221
221
|
end
|
222
222
|
end
|
223
223
|
end
|
224
224
|
|
225
|
-
describe
|
226
|
-
it
|
225
|
+
describe "#hmac_sha1_signature" do
|
226
|
+
it "reproduces a successful Twitter GET" do
|
227
227
|
options = {
|
228
228
|
:consumer_key => '8karQBlMg6gFOwcf8kcoYw',
|
229
229
|
:consumer_secret => '3d0vcHyUiiqADpWxolW8nlDIpSWMlyK7YNgc5Qna2M',
|
@@ -234,10 +234,10 @@ describe SimpleOAuth::Header do
|
|
234
234
|
:token_secret => 'T5qa1tF57tfDzKmpM89DHsNuhgOY4NT6DlNLsTFcuQ'
|
235
235
|
}
|
236
236
|
header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friends.json', {}, options)
|
237
|
-
header.to_s.
|
237
|
+
expect(header.to_s).to eq '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"'
|
238
238
|
end
|
239
239
|
|
240
|
-
it
|
240
|
+
it "reproduces a successful Twitter POST" do
|
241
241
|
options = {
|
242
242
|
:consumer_key => '8karQBlMg6gFOwcf8kcoYw',
|
243
243
|
:consumer_secret => '3d0vcHyUiiqADpWxolW8nlDIpSWMlyK7YNgc5Qna2M',
|
@@ -248,41 +248,41 @@ describe SimpleOAuth::Header do
|
|
248
248
|
:token_secret => 'T5qa1tF57tfDzKmpM89DHsNuhgOY4NT6DlNLsTFcuQ'
|
249
249
|
}
|
250
250
|
header = SimpleOAuth::Header.new(:post, 'https://api.twitter.com/1/statuses/update.json', {:status => 'hi, again'}, options)
|
251
|
-
header.to_s.
|
251
|
+
expect(header.to_s).to eq '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"'
|
252
252
|
end
|
253
253
|
end
|
254
254
|
|
255
|
-
describe
|
255
|
+
describe "#secret" do
|
256
256
|
let(:header){ SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friendships.json', {}) }
|
257
257
|
let(:secret){ header.send(:secret) }
|
258
258
|
|
259
|
-
it
|
259
|
+
it "combines the consumer and token secrets with an ampersand" do
|
260
260
|
header.stub(:options => {:consumer_secret => 'CONSUMER_SECRET', :token_secret => 'TOKEN_SECRET'})
|
261
|
-
secret.
|
261
|
+
expect(secret).to eq 'CONSUMER_SECRET&TOKEN_SECRET'
|
262
262
|
end
|
263
263
|
|
264
|
-
it
|
264
|
+
it "URI encodes each secret value before combination" do
|
265
265
|
header.stub(:options => {:consumer_secret => 'CONSUM#R_SECRET', :token_secret => 'TOKEN_S#CRET'})
|
266
|
-
secret.
|
266
|
+
expect(secret).to eq 'CONSUM%23R_SECRET&TOKEN_S%23CRET'
|
267
267
|
end
|
268
268
|
end
|
269
269
|
|
270
|
-
describe
|
270
|
+
describe "#signature_base" do
|
271
271
|
let(:header){ SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friendships.json', {}) }
|
272
272
|
let(:signature_base){ header.send(:signature_base) }
|
273
273
|
|
274
|
-
it
|
274
|
+
it "combines the request method, URL and normalized parameters using ampersands" do
|
275
275
|
header.stub(:method => 'METHOD', :url => 'URL', :normalized_params => 'NORMALIZED_PARAMS')
|
276
|
-
signature_base.
|
276
|
+
expect(signature_base).to eq 'METHOD&URL&NORMALIZED_PARAMS'
|
277
277
|
end
|
278
278
|
|
279
|
-
it
|
279
|
+
it "URI encodes each value before combination" do
|
280
280
|
header.stub(:method => 'ME#HOD', :url => 'U#L', :normalized_params => 'NORMAL#ZED_PARAMS')
|
281
|
-
signature_base.
|
281
|
+
expect(signature_base).to eq 'ME%23HOD&U%23L&NORMAL%23ZED_PARAMS'
|
282
282
|
end
|
283
283
|
end
|
284
284
|
|
285
|
-
describe
|
285
|
+
describe "#normalized_params" do
|
286
286
|
let(:header) do
|
287
287
|
header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friendships.json', {})
|
288
288
|
header.stub(:signature_params => [['A', '4'], ['B', '3'], ['B', '2'], ['C', '1'], ['D[]', '0 ']])
|
@@ -291,26 +291,26 @@ describe SimpleOAuth::Header do
|
|
291
291
|
let(:signature_params){ header.send(:signature_params) }
|
292
292
|
let(:normalized_params){ header.send(:normalized_params) }
|
293
293
|
|
294
|
-
it
|
295
|
-
normalized_params.
|
294
|
+
it "joins key/value pairs with equal signs and ampersands" do
|
295
|
+
expect(normalized_params).to be_a(String)
|
296
296
|
parts = normalized_params.split('&')
|
297
|
-
parts.size.
|
297
|
+
expect(parts.size).to eq signature_params.size
|
298
298
|
pairs = parts.map{|p| p.split('=') }
|
299
|
-
pairs.
|
299
|
+
expect(pairs).to be_all{|p| p.size == 2 }
|
300
300
|
end
|
301
301
|
end
|
302
302
|
|
303
|
-
describe
|
303
|
+
describe "#signature_params" do
|
304
304
|
let(:header){ SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friendships.json', {}) }
|
305
305
|
let(:signature_params){ header.send(:signature_params) }
|
306
306
|
|
307
|
-
it
|
307
|
+
it "combines OAuth header attributes, body parameters and URL parameters into an flattened array of key/value pairs" do
|
308
308
|
header.stub(
|
309
309
|
:attributes => {:attribute => 'ATTRIBUTE'},
|
310
310
|
:params => {'param' => 'PARAM'},
|
311
311
|
:url_params => [['url_param', '1'], ['url_param', '2']]
|
312
312
|
)
|
313
|
-
signature_params.
|
313
|
+
expect(signature_params).to eq [
|
314
314
|
[:attribute, 'ATTRIBUTE'],
|
315
315
|
['param', 'PARAM'],
|
316
316
|
['url_param', '1'],
|
@@ -319,25 +319,25 @@ describe SimpleOAuth::Header do
|
|
319
319
|
end
|
320
320
|
end
|
321
321
|
|
322
|
-
describe
|
323
|
-
it
|
322
|
+
describe "#url_params" do
|
323
|
+
it "returns an empty array when the URL has no query parameters" do
|
324
324
|
header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friendships.json', {})
|
325
|
-
header.send(:url_params).
|
325
|
+
expect(header.send(:url_params)).to eq []
|
326
326
|
end
|
327
327
|
|
328
|
-
it
|
328
|
+
it "returns an array of key/value pairs for each query parameter" do
|
329
329
|
header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friendships.json?test=TEST', {})
|
330
|
-
header.send(:url_params).
|
330
|
+
expect(header.send(:url_params)).to eq [['test', 'TEST']]
|
331
331
|
end
|
332
332
|
|
333
|
-
it
|
333
|
+
it "sorts values for repeated keys" do
|
334
334
|
header = SimpleOAuth::Header.new(:get, 'https://api.twitter.com/1/statuses/friendships.json?test=3&test=1&test=2', {})
|
335
|
-
header.send(:url_params).
|
335
|
+
expect(header.send(:url_params)).to eq [['test', '1'], ['test', '2'], ['test', '3']]
|
336
336
|
end
|
337
337
|
end
|
338
338
|
|
339
|
-
describe
|
340
|
-
it
|
339
|
+
describe "#rsa_sha1_signature" do
|
340
|
+
it "reproduces a successful OAuth example GET" do
|
341
341
|
options = {
|
342
342
|
:consumer_key => 'dpf43f3p2l4k3l03',
|
343
343
|
:consumer_secret => rsa_private_key,
|
@@ -346,16 +346,16 @@ describe SimpleOAuth::Header do
|
|
346
346
|
:timestamp => '1196666512'
|
347
347
|
}
|
348
348
|
header = SimpleOAuth::Header.new(:get, 'http://photos.example.net/photos', {:file => 'vacaction.jpg', :size => 'original'}, options)
|
349
|
-
header.to_s.
|
349
|
+
expect(header.to_s).to eq '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"'
|
350
350
|
end
|
351
351
|
end
|
352
352
|
|
353
|
-
describe
|
353
|
+
describe "#private_key" do
|
354
354
|
pending
|
355
355
|
end
|
356
356
|
|
357
|
-
describe
|
358
|
-
it
|
357
|
+
describe "#plaintext_signature" do
|
358
|
+
it "reproduces a successful OAuth example GET" do
|
359
359
|
options = {
|
360
360
|
:consumer_key => 'abcd',
|
361
361
|
:consumer_secret => 'efgh',
|
@@ -366,7 +366,7 @@ describe SimpleOAuth::Header do
|
|
366
366
|
:token_secret => 'mnop'
|
367
367
|
}
|
368
368
|
header = SimpleOAuth::Header.new(:get, 'http://host.net/resource?name=value', {:name => 'value'}, options)
|
369
|
-
header.to_s.
|
369
|
+
expect(header.to_s).to eq '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"'
|
370
370
|
end
|
371
371
|
end
|
372
372
|
end
|
metadata
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: simple_oauth
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.9
|
5
4
|
prerelease:
|
5
|
+
version: 0.2.0
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Steve Richert
|
@@ -10,56 +10,56 @@ authors:
|
|
10
10
|
autorequire:
|
11
11
|
bindir: bin
|
12
12
|
cert_chain: []
|
13
|
-
date: 2012-
|
13
|
+
date: 2012-12-02 00:00:00.000000000 Z
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
|
-
|
17
|
-
requirement: !ruby/object:Gem::Requirement
|
18
|
-
none: false
|
16
|
+
version_requirements: !ruby/object:Gem::Requirement
|
19
17
|
requirements:
|
20
18
|
- - ! '>='
|
21
19
|
- !ruby/object:Gem::Version
|
22
20
|
version: '0'
|
21
|
+
none: false
|
22
|
+
name: rake
|
23
23
|
type: :development
|
24
24
|
prerelease: false
|
25
|
-
|
26
|
-
none: false
|
25
|
+
requirement: !ruby/object:Gem::Requirement
|
27
26
|
requirements:
|
28
27
|
- - ! '>='
|
29
28
|
- !ruby/object:Gem::Version
|
30
29
|
version: '0'
|
31
|
-
- !ruby/object:Gem::Dependency
|
32
|
-
name: rspec
|
33
|
-
requirement: !ruby/object:Gem::Requirement
|
34
30
|
none: false
|
31
|
+
- !ruby/object:Gem::Dependency
|
32
|
+
version_requirements: !ruby/object:Gem::Requirement
|
35
33
|
requirements:
|
36
|
-
- -
|
34
|
+
- - ! '>='
|
37
35
|
- !ruby/object:Gem::Version
|
38
|
-
version: '2
|
36
|
+
version: '2'
|
37
|
+
none: false
|
38
|
+
name: rspec
|
39
39
|
type: :development
|
40
40
|
prerelease: false
|
41
|
-
|
42
|
-
none: false
|
41
|
+
requirement: !ruby/object:Gem::Requirement
|
43
42
|
requirements:
|
44
|
-
- -
|
43
|
+
- - ! '>='
|
45
44
|
- !ruby/object:Gem::Version
|
46
|
-
version: '2
|
47
|
-
- !ruby/object:Gem::Dependency
|
48
|
-
name: simplecov
|
49
|
-
requirement: !ruby/object:Gem::Requirement
|
45
|
+
version: '2'
|
50
46
|
none: false
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
49
|
requirements:
|
52
50
|
- - ! '>='
|
53
51
|
- !ruby/object:Gem::Version
|
54
52
|
version: '0'
|
53
|
+
none: false
|
54
|
+
name: simplecov
|
55
55
|
type: :development
|
56
56
|
prerelease: false
|
57
|
-
|
58
|
-
none: false
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
59
58
|
requirements:
|
60
59
|
- - ! '>='
|
61
60
|
- !ruby/object:Gem::Version
|
62
61
|
version: '0'
|
62
|
+
none: false
|
63
63
|
description: Simply builds and verifies OAuth headers
|
64
64
|
email:
|
65
65
|
- steve.richert@gmail.com
|
@@ -70,8 +70,10 @@ extra_rdoc_files: []
|
|
70
70
|
files:
|
71
71
|
- .gemtest
|
72
72
|
- .gitignore
|
73
|
+
- .rspec
|
73
74
|
- .travis.yml
|
74
75
|
- .yardopts
|
76
|
+
- CONTRIBUTING.md
|
75
77
|
- Gemfile
|
76
78
|
- LICENSE
|
77
79
|
- README.md
|
@@ -79,28 +81,29 @@ files:
|
|
79
81
|
- lib/simple_oauth.rb
|
80
82
|
- lib/simple_oauth/header.rb
|
81
83
|
- simple_oauth.gemspec
|
84
|
+
- spec/helper.rb
|
82
85
|
- spec/simple_oauth/header_spec.rb
|
83
|
-
- spec/spec_helper.rb
|
84
86
|
- spec/support/fixtures/rsa-private-key
|
85
87
|
- spec/support/rsa.rb
|
86
88
|
homepage: https://github.com/laserlemon/simple_oauth
|
87
|
-
licenses:
|
89
|
+
licenses:
|
90
|
+
- MIT
|
88
91
|
post_install_message:
|
89
92
|
rdoc_options: []
|
90
93
|
require_paths:
|
91
94
|
- lib
|
92
95
|
required_ruby_version: !ruby/object:Gem::Requirement
|
93
|
-
none: false
|
94
96
|
requirements:
|
95
97
|
- - ! '>='
|
96
98
|
- !ruby/object:Gem::Version
|
97
99
|
version: '0'
|
98
|
-
required_rubygems_version: !ruby/object:Gem::Requirement
|
99
100
|
none: false
|
101
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
100
102
|
requirements:
|
101
103
|
- - ! '>='
|
102
104
|
- !ruby/object:Gem::Version
|
103
105
|
version: '0'
|
106
|
+
none: false
|
104
107
|
requirements: []
|
105
108
|
rubyforge_project:
|
106
109
|
rubygems_version: 1.8.23
|