uri_signer 0.0.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.
@@ -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: