uri_signer 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,146 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '../lib/uri_signer'))
2
+
3
+ describe UriSigner::RequestSignature do
4
+ before do
5
+ @method = "GET"
6
+ @base_uri = 'https://example.com/members.json'
7
+ @query_params = { "page" => 5, "per_page" => 15 }
8
+ end
9
+
10
+ subject { described_class.new(@method, @base_uri, @query_params) }
11
+
12
+ it "responds to #http_method" do
13
+ subject.should respond_to(:http_method)
14
+ end
15
+
16
+ it "responds to #base_uri" do
17
+ subject.should respond_to(:base_uri)
18
+ end
19
+
20
+ it "responds to #encoded_base_uri" do
21
+ subject.should respond_to(:encoded_base_uri)
22
+ end
23
+
24
+ it "responds to #query_params" do
25
+ subject.should respond_to(:query_params)
26
+ end
27
+
28
+ it "responds to #query_params?" do
29
+ subject.should respond_to(:query_params?)
30
+ end
31
+
32
+ it "responds to #encoded_query_params" do
33
+ subject.should respond_to(:encoded_query_params)
34
+ end
35
+
36
+ it "responds to #signature" do
37
+ subject.should respond_to(:signature)
38
+ end
39
+
40
+ context "with duplicate values for a query string param" do
41
+ before do
42
+ @query_params = { "format" => ['json', 'json'], 'where' => ["name:nate", "name:nate"] }
43
+ end
44
+
45
+ subject { described_class.new(@method, @base_uri, @query_params) }
46
+
47
+ it "returns the encoded query params" do
48
+ subject.encoded_query_params.should == "format%3Djson%26format%3Djson%26where%3Dname%3Anate%26where%3Dname%3Anate"
49
+ end
50
+ end
51
+
52
+ context "with multiple keys in the query string" do
53
+ before do
54
+ @query_params = {"order"=>["name:desc", "id:desc"], "where"=>["name:nate", "id:123"]}
55
+ end
56
+
57
+ subject { described_class.new(@method, @base_uri, @query_params) }
58
+
59
+ it "returns the #signature string" do
60
+ subject.signature.should == "GET&https%3A%2F%2Fexample.com%2Fmembers.json&order%3Dname%3Adesc%26order%3Did%3Adesc%26where%3Dname%3Anate%26where%3Did%3A123"
61
+ end
62
+ end
63
+
64
+ context "Handling the signature" do
65
+ it "returns the #signature string" do
66
+ subject.signature.should == "GET&https%3A%2F%2Fexample.com%2Fmembers.json&page%3D5%26per_page%3D15"
67
+ end
68
+
69
+ it "removes the trailing ampersand if not query params are provided" do
70
+ sig = described_class.new(@method, @base_uri, {})
71
+ sig.signature.should == "GET&https%3A%2F%2Fexample.com%2Fmembers.json"
72
+ end
73
+ end
74
+
75
+ context "Handling the HTTP Method" do
76
+ it "returns 'GET' for the #http_method" do
77
+ subject.http_method.should == "GET"
78
+ end
79
+
80
+ it "allows you to specify the HTTP method in lowercase" do
81
+ sig = described_class.new('get', @base_uri, @query_params)
82
+ sig.http_method.should == "GET"
83
+ end
84
+ end
85
+
86
+ context "Handling the base_uri" do
87
+ it "returns 'https://example.com/members.json' for the #base_uri" do
88
+ subject.base_uri.should == "https://example.com/members.json"
89
+ end
90
+
91
+ it "returns the #encoded_base_uri" do
92
+ subject.encoded_base_uri.should == "https%3A%2F%2Fexample.com%2Fmembers.json"
93
+ end
94
+ end
95
+
96
+ context "Handling the Query Params" do
97
+ it "returns the page and per_page query params" do
98
+ subject.query_params.keys.should include('page', 'per_page')
99
+ end
100
+
101
+ it "converts the keys to strings" do
102
+ sig = described_class.new(@method, @base_uri, { :page => 4, :per_page => 35 })
103
+ sig.query_params.keys.should include('page', 'per_page')
104
+ end
105
+
106
+ it "returns true for #query_params?" do
107
+ subject.query_params?.should be_true
108
+ end
109
+
110
+ it "returns the sorted and #encoded_query_params" do
111
+ subject.encoded_query_params.should == "page%3D5%26per_page%3D15"
112
+ end
113
+ end
114
+
115
+ context "Handling when no Query Params are provided" do
116
+ subject { described_class.new(@method, @base_uri, {}) }
117
+
118
+ it "returns an empty hash for query_params" do
119
+ subject.query_params.should == {}
120
+ end
121
+
122
+ it "returns false for #query_params?" do
123
+ subject.query_params?.should be_false
124
+ end
125
+ end
126
+
127
+ context "Validating the HTTP Method" do
128
+ it "raises a UriSigner::MissingHttpMethodError when empty string is provided" do
129
+ lambda { described_class.new('', @base_uri, @query_params) }.should raise_error(UriSigner::Errors::MissingHttpMethodError)
130
+ end
131
+
132
+ it "raises a UriSigner::MissingHttpMethodError when nil is provided" do
133
+ lambda { described_class.new(nil, @base_uri, @query_params) }.should raise_error(UriSigner::Errors::MissingHttpMethodError)
134
+ end
135
+ end
136
+
137
+ context "Validating the Base URI" do
138
+ it "raises a UriSigner::MissingBaseUriError when empty string is provided" do
139
+ lambda { described_class.new(@method, '', @query_params) }.should raise_error(UriSigner::Errors::MissingBaseUriError)
140
+ end
141
+
142
+ it "raises a UriSigner::MissingBaseUriError when nil is provided" do
143
+ lambda { described_class.new(@method, nil, @query_params) }.should raise_error(UriSigner::Errors::MissingBaseUriError)
144
+ end
145
+ end
146
+ end
@@ -0,0 +1,91 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '../lib/uri_signer'))
2
+
3
+ describe UriSigner::Signer do
4
+ before do
5
+ @http_method = :get
6
+ @uri = "https://api.example.com/core/people.json?page=5&per_page=25&order=name:desc&select=id,name"
7
+ @secret = "my_secret"
8
+ end
9
+
10
+ subject { described_class.new(@http_method, @uri, @secret) }
11
+
12
+ it "responds to #uri" do
13
+ subject.should respond_to(:uri)
14
+ end
15
+
16
+ it "responds to #http_method" do
17
+ subject.should respond_to(:http_method)
18
+ end
19
+
20
+ it "responds to #signature" do
21
+ subject.should respond_to(:signature)
22
+ end
23
+
24
+ it "responds to #uri_with_signature" do
25
+ subject.should respond_to(:uri_with_signature)
26
+ end
27
+
28
+ it "responds to #valid?" do
29
+ subject.should respond_to(:valid?)
30
+ end
31
+
32
+ it "returns the #uri" do
33
+ subject.uri.should == "https://api.example.com/core/people.json?page=5&per_page=25&order=name:desc&select=id,name"
34
+ end
35
+
36
+ it "returns the upcased #http_method" do
37
+ subject.http_method.should == "GET"
38
+ end
39
+
40
+ it "returns the signed URI with the secret" do
41
+ subject.signature.should == "1AaJvChjz%2BZYJKxWsUQWNK1a%2BeGjpCs6uwQKwPw1%2FV8%3D"
42
+ end
43
+
44
+ context "appending the signature" do
45
+ it "appends the _signature on to the URI" do
46
+ subject.uri_with_signature.should == "https://api.example.com/core/people.json?page=5&per_page=25&order=name:desc&select=id,name&_signature=1AaJvChjz%2BZYJKxWsUQWNK1a%2BeGjpCs6uwQKwPw1%2FV8%3D"
47
+ end
48
+
49
+ it "appends the _signature when there are no query string params" do
50
+ uri = "https://api.example.com/core/people.json"
51
+ signer = described_class.new(@http_method, uri, @secret)
52
+ signer.uri_with_signature.should == "https://api.example.com/core/people.json?_signature=6G4xiABih7FGvjwB1JsYXoeETtBCOdshIu93X1hltzk%3D"
53
+ end
54
+ end
55
+
56
+ context "Validating the signature" do
57
+ it "returns true for #valid?" do
58
+ subject.valid?('1AaJvChjz%2BZYJKxWsUQWNK1a%2BeGjpCs6uwQKwPw1%2FV8%3D').should be_true
59
+ end
60
+
61
+ it "returns false for #valid?" do
62
+ subject.valid?('invalid').should be_false
63
+ end
64
+ end
65
+
66
+ context "Exception handling" do
67
+ it "raises a UriSigner::MissingHttpMethodError if an empty string is provided" do
68
+ lambda { described_class.new('', @uri, @secret) }.should raise_error(UriSigner::Errors::MissingHttpMethodError)
69
+ end
70
+
71
+ it "raises a UriSigner::MissingHttpMethodError if nil is provided" do
72
+ lambda { described_class.new(nil, @uri, @secret) }.should raise_error(UriSigner::Errors::MissingHttpMethodError)
73
+ end
74
+
75
+ it "raises a UriSigner::MissingUriError if an empty string is provided" do
76
+ lambda { described_class.new(@http_method, '', @secret) }.should raise_error(UriSigner::Errors::MissingUriError)
77
+ end
78
+
79
+ it "raises a UriSigner::MissingUriError if nil is provided" do
80
+ lambda { described_class.new(@http_method, nil, @secret) }.should raise_error(UriSigner::Errors::MissingUriError)
81
+ end
82
+
83
+ it "raises a UriSigner::MissingSecretError if an empty string is provided" do
84
+ lambda { described_class.new(@http_method, @uri, '') }.should raise_error(UriSigner::Errors::MissingSecretError)
85
+ end
86
+
87
+ it "raises a UriSigner::MissingSecretError if nil is provided" do
88
+ lambda { described_class.new(@http_method, @uri, nil) }.should raise_error(UriSigner::Errors::MissingSecretError)
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,12 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '../../../lib/uri_signer'))
2
+
3
+ describe UriSigner::Helpers::Hash do
4
+ before(:each) do
5
+ @hash = { :first => 'element', 'string' => 'element' }
6
+ @hash.extend(UriSigner::Helpers::Hash)
7
+ end
8
+
9
+ it "converts all keys to strings" do
10
+ @hash.stringify_keys.keys.should == ["first", "string"]
11
+ end
12
+ end
@@ -0,0 +1,41 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '../../../lib/uri_signer'))
2
+
3
+ describe UriSigner::Helpers::String do
4
+ before(:each) do
5
+ @string = "this is the string"
6
+ @string.extend(UriSigner::Helpers::String)
7
+ end
8
+
9
+ context "Escaping with Rack::Utils" do
10
+ subject { "This & That = Your's + Mine"}
11
+
12
+ it "escapes the string properly" do
13
+ subject.extend(UriSigner::Helpers::String).escaped.should == "This+%26+That+%3D+Your%27s+%2B+Mine"
14
+ end
15
+ end
16
+
17
+ context "Unescaping with Rack::Utils" do
18
+ subject { "This+%26+That+%3D+Your%27s+%2B+Mine" }
19
+
20
+ it "unescapes the string properly" do
21
+ subject.extend(UriSigner::Helpers::String).unescaped.should == "This & That = Your's + Mine"
22
+ end
23
+ end
24
+
25
+ context "Base64 encoding" do
26
+ subject { "abcd\n" }
27
+
28
+ it "base64 encodes the string and removes the newline character at the end" do
29
+ Base64.stub!(:encode64).and_return("bcdf\n")
30
+ subject.extend(UriSigner::Helpers::String).base64_encoded.should == "bcdf"
31
+ end
32
+ end
33
+
34
+ context "URI Parsed string" do
35
+ subject { "https://example.com/core/person.json" }
36
+
37
+ it "returns an Addressable parsed URI" do
38
+ subject.extend(UriSigner::Helpers::String).to_parsed_uri.should be_a_kind_of(Addressable::URI)
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,41 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '../lib/uri_signer'))
2
+
3
+ describe UriSigner::UriSignature do
4
+ before do
5
+ @signature_string = "GET&https://test.example.com"
6
+ @secret = "abcd1234"
7
+ end
8
+
9
+ subject { described_class.new(@signature_string, @secret) }
10
+
11
+ it "responds to #signature_string" do
12
+ subject.should respond_to(:signature_string)
13
+ end
14
+
15
+ it "responds to #signature" do
16
+ subject.should respond_to(:signature)
17
+ end
18
+
19
+ it "signs the request" do
20
+ # NOTE: Want to somehow refactor this when looking into the players of signing (String Helpers)
21
+ subject.signature.should == "KwLgbFRjaoQ8IBQs3xje6uhgyoT6gQR04YQs36lAXmk%3D"
22
+ end
23
+
24
+ context "Validations" do
25
+ it "raises UriSigner::MissingSignatureStringError when empty string is provided" do
26
+ lambda { described_class.new('', @secret) }.should raise_error(UriSigner::Errors::MissingSignatureStringError)
27
+ end
28
+
29
+ it "raises UriSigner::MissingSignatureStringError when nil is provided" do
30
+ lambda { described_class.new(nil, @secret) }.should raise_error(UriSigner::Errors::MissingSignatureStringError)
31
+ end
32
+
33
+ it "raises UriSigner::MissingSecretError when empty string is provided" do
34
+ lambda { described_class.new(@signature_string, '') }.should raise_error(UriSigner::Errors::MissingSecretError)
35
+ end
36
+
37
+ it "raises UriSigner::MissingSecretError when nil is provided" do
38
+ lambda { described_class.new(@signature_string, nil) }.should raise_error(UriSigner::Errors::MissingSecretError)
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,31 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'uri_signer/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "uri_signer"
8
+ spec.version = UriSigner::VERSION
9
+ spec.authors = ["Nate Klaiber"]
10
+ spec.email = ["nklaiber@kissmetrics.com"]
11
+ spec.description = %q{ Handle the generation of a URI signature for API }
12
+ spec.summary = %q{ Given a client secret, we can digitally sign the URL and make requests to the API. }
13
+ spec.homepage = "https://github.com/kissmetrics/uri_signer"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency 'activesupport', ">=3.0.0"
22
+ spec.add_dependency 'rack'
23
+ spec.add_dependency 'addressable'
24
+ spec.add_dependency 'ruby-hmac'
25
+
26
+ spec.add_development_dependency "bundler", "~> 1.3"
27
+ spec.add_development_dependency "rake"
28
+ spec.add_development_dependency 'rspec'
29
+ spec.add_development_dependency 'yard'
30
+ spec.add_development_dependency 'redcarpet'
31
+ end
metadata ADDED
@@ -0,0 +1,237 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: uri_signer
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Nate Klaiber
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-04-12 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: activesupport
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 3.0.0
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: 3.0.0
30
+ - !ruby/object:Gem::Dependency
31
+ name: rack
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: addressable
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: ruby-hmac
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :runtime
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: bundler
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ~>
84
+ - !ruby/object:Gem::Version
85
+ version: '1.3'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ~>
92
+ - !ruby/object:Gem::Version
93
+ version: '1.3'
94
+ - !ruby/object:Gem::Dependency
95
+ name: rake
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ - !ruby/object:Gem::Dependency
111
+ name: rspec
112
+ requirement: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ! '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ! '>='
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ - !ruby/object:Gem::Dependency
127
+ name: yard
128
+ requirement: !ruby/object:Gem::Requirement
129
+ none: false
130
+ requirements:
131
+ - - ! '>='
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ type: :development
135
+ prerelease: false
136
+ version_requirements: !ruby/object:Gem::Requirement
137
+ none: false
138
+ requirements:
139
+ - - ! '>='
140
+ - !ruby/object:Gem::Version
141
+ version: '0'
142
+ - !ruby/object:Gem::Dependency
143
+ name: redcarpet
144
+ requirement: !ruby/object:Gem::Requirement
145
+ none: false
146
+ requirements:
147
+ - - ! '>='
148
+ - !ruby/object:Gem::Version
149
+ version: '0'
150
+ type: :development
151
+ prerelease: false
152
+ version_requirements: !ruby/object:Gem::Requirement
153
+ none: false
154
+ requirements:
155
+ - - ! '>='
156
+ - !ruby/object:Gem::Version
157
+ version: '0'
158
+ description: ! ' Handle the generation of a URI signature for API '
159
+ email:
160
+ - nklaiber@kissmetrics.com
161
+ executables: []
162
+ extensions: []
163
+ extra_rdoc_files: []
164
+ files:
165
+ - .gitignore
166
+ - .rvmrc.sample
167
+ - Gemfile
168
+ - LICENSE.txt
169
+ - README.md
170
+ - Rakefile
171
+ - lib/uri_signer.rb
172
+ - lib/uri_signer/errors.rb
173
+ - lib/uri_signer/errors/missing_base_uri_error.rb
174
+ - lib/uri_signer/errors/missing_http_method_error.rb
175
+ - lib/uri_signer/errors/missing_query_hash_error.rb
176
+ - lib/uri_signer/errors/missing_secret_error.rb
177
+ - lib/uri_signer/errors/missing_signature_string_error.rb
178
+ - lib/uri_signer/errors/missing_uri_error.rb
179
+ - lib/uri_signer/helpers.rb
180
+ - lib/uri_signer/helpers/hash.rb
181
+ - lib/uri_signer/helpers/string.rb
182
+ - lib/uri_signer/query_hash_parser.rb
183
+ - lib/uri_signer/request_parser.rb
184
+ - lib/uri_signer/request_signature.rb
185
+ - lib/uri_signer/signer.rb
186
+ - lib/uri_signer/uri_signature.rb
187
+ - lib/uri_signer/version.rb
188
+ - reload_yard
189
+ - spec/query_hash_parser_spec.rb
190
+ - spec/request_parser_spec.rb
191
+ - spec/request_signature_spec.rb
192
+ - spec/signer_spec.rb
193
+ - spec/unit/helpers/hash_spec.rb
194
+ - spec/unit/helpers/string_spec.rb
195
+ - spec/uri_signature_spec.rb
196
+ - uri_signer.gemspec
197
+ homepage: https://github.com/kissmetrics/uri_signer
198
+ licenses:
199
+ - MIT
200
+ post_install_message:
201
+ rdoc_options: []
202
+ require_paths:
203
+ - lib
204
+ required_ruby_version: !ruby/object:Gem::Requirement
205
+ none: false
206
+ requirements:
207
+ - - ! '>='
208
+ - !ruby/object:Gem::Version
209
+ version: '0'
210
+ segments:
211
+ - 0
212
+ hash: 538882935789920834
213
+ required_rubygems_version: !ruby/object:Gem::Requirement
214
+ none: false
215
+ requirements:
216
+ - - ! '>='
217
+ - !ruby/object:Gem::Version
218
+ version: '0'
219
+ segments:
220
+ - 0
221
+ hash: 538882935789920834
222
+ requirements: []
223
+ rubyforge_project:
224
+ rubygems_version: 1.8.25
225
+ signing_key:
226
+ specification_version: 3
227
+ summary: Given a client secret, we can digitally sign the URL and make requests to
228
+ the API.
229
+ test_files:
230
+ - spec/query_hash_parser_spec.rb
231
+ - spec/request_parser_spec.rb
232
+ - spec/request_signature_spec.rb
233
+ - spec/signer_spec.rb
234
+ - spec/unit/helpers/hash_spec.rb
235
+ - spec/unit/helpers/string_spec.rb
236
+ - spec/uri_signature_spec.rb
237
+ has_rdoc: