scout 5.7.1 → 5.7.2.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (251) hide show
  1. data/CHANGELOG.markdown +5 -0
  2. data/lib/scout/command/run.rb +1 -1
  3. data/lib/scout/server.rb +2 -1
  4. data/lib/scout/streamer.rb +3 -2
  5. data/lib/scout/streamer_daemon.rb +4 -4
  6. data/lib/scout/version.rb +1 -1
  7. data/test/scout_test.rb +7 -8
  8. data/vendor/httpclient/README.txt +759 -0
  9. data/vendor/httpclient/bin/httpclient +65 -0
  10. data/vendor/httpclient/lib/hexdump.rb +50 -0
  11. data/vendor/httpclient/lib/http-access2.rb +55 -0
  12. data/vendor/httpclient/lib/http-access2/cookie.rb +1 -0
  13. data/vendor/httpclient/lib/http-access2/http.rb +1 -0
  14. data/vendor/httpclient/lib/httpclient.rb +1156 -0
  15. data/vendor/httpclient/lib/httpclient/auth.rb +899 -0
  16. data/vendor/httpclient/lib/httpclient/cacert.p7s +1912 -0
  17. data/vendor/httpclient/lib/httpclient/connection.rb +88 -0
  18. data/vendor/httpclient/lib/httpclient/cookie.rb +438 -0
  19. data/vendor/httpclient/lib/httpclient/http.rb +1046 -0
  20. data/vendor/httpclient/lib/httpclient/include_client.rb +83 -0
  21. data/vendor/httpclient/lib/httpclient/session.rb +1025 -0
  22. data/vendor/httpclient/lib/httpclient/ssl_config.rb +403 -0
  23. data/vendor/httpclient/lib/httpclient/timeout.rb +140 -0
  24. data/vendor/httpclient/lib/httpclient/util.rb +178 -0
  25. data/vendor/httpclient/lib/httpclient/version.rb +3 -0
  26. data/vendor/httpclient/lib/oauthclient.rb +110 -0
  27. data/vendor/httpclient/sample/async.rb +8 -0
  28. data/vendor/httpclient/sample/auth.rb +11 -0
  29. data/vendor/httpclient/sample/cookie.rb +18 -0
  30. data/vendor/httpclient/sample/dav.rb +103 -0
  31. data/vendor/httpclient/sample/howto.rb +49 -0
  32. data/vendor/httpclient/sample/oauth_buzz.rb +57 -0
  33. data/vendor/httpclient/sample/oauth_friendfeed.rb +59 -0
  34. data/vendor/httpclient/sample/oauth_salesforce_10.rb +63 -0
  35. data/vendor/httpclient/sample/oauth_twitter.rb +61 -0
  36. data/vendor/httpclient/sample/ssl/0cert.pem +22 -0
  37. data/vendor/httpclient/sample/ssl/0key.pem +30 -0
  38. data/vendor/httpclient/sample/ssl/1000cert.pem +19 -0
  39. data/vendor/httpclient/sample/ssl/1000key.pem +18 -0
  40. data/vendor/httpclient/sample/ssl/htdocs/index.html +10 -0
  41. data/vendor/httpclient/sample/ssl/ssl_client.rb +22 -0
  42. data/vendor/httpclient/sample/ssl/webrick_httpsd.rb +29 -0
  43. data/vendor/httpclient/sample/stream.rb +21 -0
  44. data/vendor/httpclient/sample/thread.rb +27 -0
  45. data/vendor/httpclient/sample/wcat.rb +21 -0
  46. data/vendor/httpclient/test/ca-chain.cert +44 -0
  47. data/vendor/httpclient/test/ca.cert +23 -0
  48. data/vendor/httpclient/test/client.cert +19 -0
  49. data/vendor/httpclient/test/client.key +15 -0
  50. data/vendor/httpclient/test/helper.rb +129 -0
  51. data/vendor/httpclient/test/htdigest +1 -0
  52. data/vendor/httpclient/test/htpasswd +2 -0
  53. data/vendor/httpclient/test/runner.rb +2 -0
  54. data/vendor/httpclient/test/server.cert +19 -0
  55. data/vendor/httpclient/test/server.key +15 -0
  56. data/vendor/httpclient/test/sslsvr.rb +65 -0
  57. data/vendor/httpclient/test/subca.cert +21 -0
  58. data/vendor/httpclient/test/test_auth.rb +321 -0
  59. data/vendor/httpclient/test/test_cookie.rb +391 -0
  60. data/vendor/httpclient/test/test_hexdump.rb +14 -0
  61. data/vendor/httpclient/test/test_http-access2.rb +507 -0
  62. data/vendor/httpclient/test/test_httpclient.rb +1783 -0
  63. data/vendor/httpclient/test/test_include_client.rb +52 -0
  64. data/vendor/httpclient/test/test_ssl.rb +235 -0
  65. data/vendor/multi_json/.document +5 -0
  66. data/vendor/multi_json/.rspec +3 -0
  67. data/vendor/multi_json/.travis.yml +11 -0
  68. data/vendor/multi_json/.yardopts +6 -0
  69. data/vendor/multi_json/CHANGELOG.md +169 -0
  70. data/vendor/multi_json/CONTRIBUTING.md +46 -0
  71. data/vendor/multi_json/Gemfile +31 -0
  72. data/vendor/multi_json/LICENSE.md +20 -0
  73. data/vendor/multi_json/README.md +109 -0
  74. data/vendor/multi_json/Rakefile +12 -0
  75. data/vendor/multi_json/lib/multi_json.rb +157 -0
  76. data/vendor/multi_json/lib/multi_json/adapter.rb +48 -0
  77. data/vendor/multi_json/lib/multi_json/adapters/gson.rb +19 -0
  78. data/vendor/multi_json/lib/multi_json/adapters/jr_jackson.rb +19 -0
  79. data/vendor/multi_json/lib/multi_json/adapters/json_common.rb +25 -0
  80. data/vendor/multi_json/lib/multi_json/adapters/json_gem.rb +11 -0
  81. data/vendor/multi_json/lib/multi_json/adapters/json_pure.rb +11 -0
  82. data/vendor/multi_json/lib/multi_json/adapters/nsjsonserialization.rb +34 -0
  83. data/vendor/multi_json/lib/multi_json/adapters/oj.rb +24 -0
  84. data/vendor/multi_json/lib/multi_json/adapters/ok_json.rb +22 -0
  85. data/vendor/multi_json/lib/multi_json/adapters/yajl.rb +19 -0
  86. data/vendor/multi_json/lib/multi_json/convertible_hash_keys.rb +43 -0
  87. data/vendor/multi_json/lib/multi_json/load_error.rb +11 -0
  88. data/vendor/multi_json/lib/multi_json/options.rb +48 -0
  89. data/vendor/multi_json/lib/multi_json/vendor/okjson.rb +606 -0
  90. data/vendor/multi_json/lib/multi_json/version.rb +20 -0
  91. data/vendor/multi_json/multi_json.gemspec +22 -0
  92. data/vendor/multi_json/spec/adapter_shared_example.rb +235 -0
  93. data/vendor/multi_json/spec/has_options.rb +119 -0
  94. data/vendor/multi_json/spec/helper.rb +35 -0
  95. data/vendor/multi_json/spec/json_common_shared_example.rb +30 -0
  96. data/vendor/multi_json/spec/multi_json_spec.rb +226 -0
  97. data/vendor/{signature → pusher}/.document +0 -0
  98. data/vendor/{json_pure/diagrams/.keep → pusher/.gemtest} +0 -0
  99. data/vendor/pusher/.gitignore +23 -0
  100. data/vendor/pusher/.travis.yml +15 -0
  101. data/vendor/pusher/Gemfile +2 -0
  102. data/vendor/{pusher-gem → pusher}/LICENSE +1 -1
  103. data/vendor/pusher/README.md +186 -0
  104. data/vendor/{pusher-gem → pusher}/Rakefile +0 -0
  105. data/vendor/{pusher-gem → pusher}/examples/async_message.rb +0 -0
  106. data/vendor/pusher/lib/pusher.rb +60 -0
  107. data/vendor/{pusher-gem → pusher}/lib/pusher/channel.rb +47 -54
  108. data/vendor/pusher/lib/pusher/client.rb +306 -0
  109. data/vendor/pusher/lib/pusher/request.rb +107 -0
  110. data/vendor/pusher/lib/pusher/resource.rb +36 -0
  111. data/vendor/pusher/lib/pusher/webhook.rb +110 -0
  112. data/vendor/{pusher-gem → pusher}/pusher.gemspec +6 -5
  113. data/vendor/pusher/spec/channel_spec.rb +127 -0
  114. data/vendor/pusher/spec/client_spec.rb +464 -0
  115. data/vendor/{pusher-gem → pusher}/spec/spec_helper.rb +12 -0
  116. data/vendor/pusher/spec/web_hook_spec.rb +117 -0
  117. data/vendor/signature/.travis.yml +15 -0
  118. data/vendor/signature/Gemfile +1 -1
  119. data/vendor/signature/README.md +38 -28
  120. data/vendor/signature/lib/signature.rb +97 -15
  121. data/vendor/signature/lib/signature/query_encoder.rb +47 -0
  122. data/vendor/signature/lib/signature/version.rb +1 -1
  123. data/vendor/signature/signature.gemspec +3 -2
  124. data/vendor/signature/spec/signature_spec.rb +164 -55
  125. data/vendor/signature/spec/spec_helper.rb +2 -3
  126. metadata +120 -145
  127. data/vendor/json_pure/.gitignore +0 -12
  128. data/vendor/json_pure/.travis.yml +0 -20
  129. data/vendor/json_pure/CHANGES +0 -282
  130. data/vendor/json_pure/COPYING +0 -58
  131. data/vendor/json_pure/COPYING-json-jruby +0 -57
  132. data/vendor/json_pure/GPL +0 -340
  133. data/vendor/json_pure/Gemfile +0 -11
  134. data/vendor/json_pure/README-json-jruby.markdown +0 -33
  135. data/vendor/json_pure/README.rdoc +0 -358
  136. data/vendor/json_pure/Rakefile +0 -412
  137. data/vendor/json_pure/TODO +0 -1
  138. data/vendor/json_pure/VERSION +0 -1
  139. data/vendor/json_pure/data/example.json +0 -1
  140. data/vendor/json_pure/data/index.html +0 -38
  141. data/vendor/json_pure/data/prototype.js +0 -4184
  142. data/vendor/json_pure/ext/json/ext/fbuffer/fbuffer.h +0 -181
  143. data/vendor/json_pure/ext/json/ext/generator/depend +0 -1
  144. data/vendor/json_pure/ext/json/ext/generator/extconf.rb +0 -14
  145. data/vendor/json_pure/ext/json/ext/generator/generator.c +0 -1435
  146. data/vendor/json_pure/ext/json/ext/generator/generator.h +0 -148
  147. data/vendor/json_pure/ext/json/ext/parser/depend +0 -1
  148. data/vendor/json_pure/ext/json/ext/parser/extconf.rb +0 -13
  149. data/vendor/json_pure/ext/json/ext/parser/parser.c +0 -2204
  150. data/vendor/json_pure/ext/json/ext/parser/parser.h +0 -77
  151. data/vendor/json_pure/ext/json/ext/parser/parser.rl +0 -927
  152. data/vendor/json_pure/install.rb +0 -23
  153. data/vendor/json_pure/java/src/json/ext/ByteListTranscoder.java +0 -167
  154. data/vendor/json_pure/java/src/json/ext/Generator.java +0 -444
  155. data/vendor/json_pure/java/src/json/ext/GeneratorMethods.java +0 -232
  156. data/vendor/json_pure/java/src/json/ext/GeneratorService.java +0 -43
  157. data/vendor/json_pure/java/src/json/ext/GeneratorState.java +0 -543
  158. data/vendor/json_pure/java/src/json/ext/OptionsReader.java +0 -114
  159. data/vendor/json_pure/java/src/json/ext/Parser.java +0 -2644
  160. data/vendor/json_pure/java/src/json/ext/Parser.rl +0 -968
  161. data/vendor/json_pure/java/src/json/ext/ParserService.java +0 -35
  162. data/vendor/json_pure/java/src/json/ext/RuntimeInfo.java +0 -121
  163. data/vendor/json_pure/java/src/json/ext/StringDecoder.java +0 -167
  164. data/vendor/json_pure/java/src/json/ext/StringEncoder.java +0 -106
  165. data/vendor/json_pure/java/src/json/ext/Utils.java +0 -89
  166. data/vendor/json_pure/json-java.gemspec +0 -23
  167. data/vendor/json_pure/json.gemspec +0 -37
  168. data/vendor/json_pure/json_pure.gemspec +0 -39
  169. data/vendor/json_pure/lib/json.rb +0 -62
  170. data/vendor/json_pure/lib/json/add/bigdecimal.rb +0 -28
  171. data/vendor/json_pure/lib/json/add/complex.rb +0 -22
  172. data/vendor/json_pure/lib/json/add/core.rb +0 -11
  173. data/vendor/json_pure/lib/json/add/date.rb +0 -34
  174. data/vendor/json_pure/lib/json/add/date_time.rb +0 -50
  175. data/vendor/json_pure/lib/json/add/exception.rb +0 -31
  176. data/vendor/json_pure/lib/json/add/ostruct.rb +0 -31
  177. data/vendor/json_pure/lib/json/add/range.rb +0 -29
  178. data/vendor/json_pure/lib/json/add/rational.rb +0 -22
  179. data/vendor/json_pure/lib/json/add/regexp.rb +0 -30
  180. data/vendor/json_pure/lib/json/add/struct.rb +0 -30
  181. data/vendor/json_pure/lib/json/add/symbol.rb +0 -25
  182. data/vendor/json_pure/lib/json/add/time.rb +0 -38
  183. data/vendor/json_pure/lib/json/common.rb +0 -487
  184. data/vendor/json_pure/lib/json/ext.rb +0 -21
  185. data/vendor/json_pure/lib/json/ext/.keep +0 -0
  186. data/vendor/json_pure/lib/json/generic_object.rb +0 -70
  187. data/vendor/json_pure/lib/json/pure.rb +0 -21
  188. data/vendor/json_pure/lib/json/pure/generator.rb +0 -522
  189. data/vendor/json_pure/lib/json/pure/parser.rb +0 -359
  190. data/vendor/json_pure/lib/json/version.rb +0 -8
  191. data/vendor/json_pure/tests/fixtures/fail1.json +0 -1
  192. data/vendor/json_pure/tests/fixtures/fail10.json +0 -1
  193. data/vendor/json_pure/tests/fixtures/fail11.json +0 -1
  194. data/vendor/json_pure/tests/fixtures/fail12.json +0 -1
  195. data/vendor/json_pure/tests/fixtures/fail13.json +0 -1
  196. data/vendor/json_pure/tests/fixtures/fail14.json +0 -1
  197. data/vendor/json_pure/tests/fixtures/fail18.json +0 -1
  198. data/vendor/json_pure/tests/fixtures/fail19.json +0 -1
  199. data/vendor/json_pure/tests/fixtures/fail2.json +0 -1
  200. data/vendor/json_pure/tests/fixtures/fail20.json +0 -1
  201. data/vendor/json_pure/tests/fixtures/fail21.json +0 -1
  202. data/vendor/json_pure/tests/fixtures/fail22.json +0 -1
  203. data/vendor/json_pure/tests/fixtures/fail23.json +0 -1
  204. data/vendor/json_pure/tests/fixtures/fail24.json +0 -1
  205. data/vendor/json_pure/tests/fixtures/fail25.json +0 -1
  206. data/vendor/json_pure/tests/fixtures/fail27.json +0 -2
  207. data/vendor/json_pure/tests/fixtures/fail28.json +0 -2
  208. data/vendor/json_pure/tests/fixtures/fail3.json +0 -1
  209. data/vendor/json_pure/tests/fixtures/fail4.json +0 -1
  210. data/vendor/json_pure/tests/fixtures/fail5.json +0 -1
  211. data/vendor/json_pure/tests/fixtures/fail6.json +0 -1
  212. data/vendor/json_pure/tests/fixtures/fail7.json +0 -1
  213. data/vendor/json_pure/tests/fixtures/fail8.json +0 -1
  214. data/vendor/json_pure/tests/fixtures/fail9.json +0 -1
  215. data/vendor/json_pure/tests/fixtures/pass1.json +0 -56
  216. data/vendor/json_pure/tests/fixtures/pass15.json +0 -1
  217. data/vendor/json_pure/tests/fixtures/pass16.json +0 -1
  218. data/vendor/json_pure/tests/fixtures/pass17.json +0 -1
  219. data/vendor/json_pure/tests/fixtures/pass2.json +0 -1
  220. data/vendor/json_pure/tests/fixtures/pass26.json +0 -1
  221. data/vendor/json_pure/tests/fixtures/pass3.json +0 -6
  222. data/vendor/json_pure/tests/setup_variant.rb +0 -11
  223. data/vendor/json_pure/tests/test_json.rb +0 -545
  224. data/vendor/json_pure/tests/test_json_addition.rb +0 -196
  225. data/vendor/json_pure/tests/test_json_encoding.rb +0 -65
  226. data/vendor/json_pure/tests/test_json_fixtures.rb +0 -35
  227. data/vendor/json_pure/tests/test_json_generate.rb +0 -322
  228. data/vendor/json_pure/tests/test_json_generic_object.rb +0 -75
  229. data/vendor/json_pure/tests/test_json_string_matching.rb +0 -39
  230. data/vendor/json_pure/tests/test_json_unicode.rb +0 -72
  231. data/vendor/json_pure/tools/fuzz.rb +0 -139
  232. data/vendor/json_pure/tools/server.rb +0 -62
  233. data/vendor/pusher-gem/Gemfile +0 -2
  234. data/vendor/pusher-gem/README.md +0 -80
  235. data/vendor/pusher-gem/lib/pusher.rb +0 -107
  236. data/vendor/pusher-gem/lib/pusher/request.rb +0 -107
  237. data/vendor/pusher-gem/spec/channel_spec.rb +0 -274
  238. data/vendor/pusher-gem/spec/pusher_spec.rb +0 -87
  239. data/vendor/ruby-hmac/History.txt +0 -15
  240. data/vendor/ruby-hmac/Manifest.txt +0 -11
  241. data/vendor/ruby-hmac/README.md +0 -41
  242. data/vendor/ruby-hmac/Rakefile +0 -23
  243. data/vendor/ruby-hmac/lib/hmac-md5.rb +0 -11
  244. data/vendor/ruby-hmac/lib/hmac-rmd160.rb +0 -11
  245. data/vendor/ruby-hmac/lib/hmac-sha1.rb +0 -11
  246. data/vendor/ruby-hmac/lib/hmac-sha2.rb +0 -25
  247. data/vendor/ruby-hmac/lib/hmac.rb +0 -118
  248. data/vendor/ruby-hmac/lib/ruby_hmac.rb +0 -2
  249. data/vendor/ruby-hmac/ruby-hmac.gemspec +0 -33
  250. data/vendor/ruby-hmac/test/test_hmac.rb +0 -89
  251. data/vendor/signature/VERSION +0 -1
@@ -11,3 +11,15 @@ require 'webmock/rspec'
11
11
 
12
12
  require 'pusher'
13
13
  require 'eventmachine'
14
+
15
+ RSpec.configure do |config|
16
+ config.before(:each) do
17
+ WebMock.reset!
18
+ WebMock.disable_net_connect!
19
+ end
20
+ end
21
+
22
+ def hmac(key, data)
23
+ digest = OpenSSL::Digest::SHA256.new
24
+ OpenSSL::HMAC.hexdigest(digest, key, data)
25
+ end
@@ -0,0 +1,117 @@
1
+ require 'spec_helper'
2
+
3
+ require 'rack'
4
+ require 'stringio'
5
+
6
+ describe Pusher::WebHook do
7
+ before :each do
8
+ @hook_data = {
9
+ "time_ms" => 123456,
10
+ "events" => [
11
+ {"name" => 'foo'}
12
+ ]
13
+ }
14
+ end
15
+
16
+ describe "initialization" do
17
+ it "can be initialized with Rack::Request" do
18
+ request = Rack::Request.new({
19
+ 'HTTP_X_PUSHER_KEY' => '1234',
20
+ 'HTTP_X_PUSHER_SIGNATURE' => 'asdf',
21
+ 'CONTENT_TYPE' => 'application/json',
22
+ 'rack.input' => StringIO.new(MultiJson.encode(@hook_data))
23
+ })
24
+ wh = Pusher::WebHook.new(request)
25
+ wh.key.should == '1234'
26
+ wh.signature.should == 'asdf'
27
+ wh.data.should == @hook_data
28
+ end
29
+
30
+ it "can be initialized with a hash" do
31
+ request = {
32
+ :key => '1234',
33
+ :signature => 'asdf',
34
+ :content_type => 'application/json',
35
+ :body => MultiJson.encode(@hook_data),
36
+ }
37
+ wh = Pusher::WebHook.new(request)
38
+ wh.key.should == '1234'
39
+ wh.signature.should == 'asdf'
40
+ wh.data.should == @hook_data
41
+ end
42
+ end
43
+
44
+ describe "after initialization" do
45
+ before :each do
46
+ @body = MultiJson.encode(@hook_data)
47
+ request = {
48
+ :key => '1234',
49
+ :signature => hmac('asdf', @body),
50
+ :content_type => 'application/json',
51
+ :body => @body
52
+ }
53
+
54
+ @client = Pusher::Client.new
55
+ @wh = Pusher::WebHook.new(request, @client)
56
+ end
57
+
58
+ it "should validate" do
59
+ @client.key = '1234'
60
+ @client.secret = 'asdf'
61
+ @wh.should be_valid
62
+ end
63
+
64
+ it "should not validate if key is wrong" do
65
+ @client.key = '12345'
66
+ @client.secret = 'asdf'
67
+ Pusher.logger.should_receive(:warn).with("Received webhook with unknown key: 1234")
68
+ @wh.should_not be_valid
69
+ end
70
+
71
+ it "should not validate if secret is wrong" do
72
+ @client.key = '1234'
73
+ @client.secret = 'asdfxxx'
74
+ digest = OpenSSL::Digest::SHA256.new
75
+ expected = OpenSSL::HMAC.hexdigest(digest, @client.secret, @body)
76
+ Pusher.logger.should_receive(:warn).with("Received WebHook with invalid signature: got #{@wh.signature}, expected #{expected}")
77
+ @wh.should_not be_valid
78
+ end
79
+
80
+ it "should validate with an extra token" do
81
+ @client.key = '12345'
82
+ @client.secret = 'xxx'
83
+ @wh.valid?({:key => '1234', :secret => 'asdf'}).should be_true
84
+ end
85
+
86
+ it "should validate with an array of extra tokens" do
87
+ @client.key = '123456'
88
+ @client.secret = 'xxx'
89
+ @wh.valid?([
90
+ {:key => '12345', :secret => 'wtf'},
91
+ {:key => '1234', :secret => 'asdf'}
92
+ ]).should be_true
93
+ end
94
+
95
+ it "should not validate if all keys are wrong with extra tokens" do
96
+ @client.key = '123456'
97
+ @client.secret = 'asdf'
98
+ Pusher.logger.should_receive(:warn).with("Received webhook with unknown key: 1234")
99
+ @wh.valid?({:key => '12345', :secret => 'asdf'}).should be_false
100
+ end
101
+
102
+ it "should not validate if secret is wrong with extra tokens" do
103
+ @client.key = '123456'
104
+ @client.secret = 'asdfxxx'
105
+ Pusher.logger.should_receive(:warn).with(/Received WebHook with invalid signature/)
106
+ @wh.valid?({:key => '1234', :secret => 'wtf'}).should be_false
107
+ end
108
+
109
+ it "should expose events" do
110
+ @wh.events.should == @hook_data["events"]
111
+ end
112
+
113
+ it "should expose time" do
114
+ @wh.time.should == Time.at(123.456)
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,15 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.8.7
4
+ - 1.9.2
5
+ - 1.9.3
6
+ - jruby-18mode # JRuby in 1.8 mode
7
+ - jruby-19mode # JRuby in 1.9 mode
8
+ - rbx-18mode
9
+ - rbx-19mode
10
+ matrix:
11
+ allow_failures:
12
+ - rvm: rbx-18mode
13
+ - rvm: rbx-19mode
14
+
15
+ script: bundle exec rspec spec
@@ -1,3 +1,3 @@
1
- source :rubygems
1
+ source 'https://rubygems.org'
2
2
 
3
3
  gemspec
@@ -1,47 +1,55 @@
1
1
  signature
2
2
  =========
3
3
 
4
+ [![Build Status](https://secure.travis-ci.org/mloughran/signature.png?branch=master)](http://travis-ci.org/mloughran/signature)
5
+
4
6
  Examples
5
7
  --------
6
8
 
7
9
  Client example
8
10
 
9
- params = {:some => 'parameters'}
10
- token = Signature::Token.new('my_key', 'my_secret')
11
- request = Signature::Request.new('POST', '/api/thing', params)
12
- auth_hash = request.sign(token)
13
- query_params = params.merge(auth_hash)
14
-
15
- HTTParty.post('http://myservice/api/thing', {
16
- :query => query_params
17
- })
11
+ ```ruby
12
+ params = {:some => 'parameters'}
13
+ token = Signature::Token.new('my_key', 'my_secret')
14
+ request = Signature::Request.new('POST', '/api/thing', params)
15
+ auth_hash = request.sign(token)
16
+ query_params = params.merge(auth_hash)
17
+
18
+ HTTParty.post('http://myservice/api/thing', {
19
+ :query => query_params
20
+ })
21
+ ```
18
22
 
19
23
  `query_params` looks like:
20
24
 
21
- {
22
- :some => "parameters",
23
- :auth_timestamp => 1273231888,
24
- :auth_signature => "28b6bb0f242f71064916fad6ae463fe91f5adc302222dfc02c348ae1941eaf80",
25
- :auth_version => "1.0",
26
- :auth_key => "my_key"
27
- }
25
+ ```ruby
26
+ {
27
+ :some => "parameters",
28
+ :auth_timestamp => 1273231888,
29
+ :auth_signature => "28b6bb0f242f71064916fad6ae463fe91f5adc302222dfc02c348ae1941eaf80",
30
+ :auth_version => "1.0",
31
+ :auth_key => "my_key"
32
+ }
28
33
 
34
+ ```
29
35
  Server example (sinatra)
30
36
 
31
- error Signature::AuthenticationError do |controller|
32
- error = controller.env["sinatra.error"]
33
- halt 401, "401 UNAUTHORIZED: #{error.message}\n"
34
- end
37
+ ```ruby
38
+ error Signature::AuthenticationError do |controller|
39
+ error = controller.env["sinatra.error"]
40
+ halt 401, "401 UNAUTHORIZED: #{error.message}\n"
41
+ end
35
42
 
36
- post '/api/thing' do
37
- request = Signature::Request.new('POST', env["REQUEST_PATH"], params)
38
- # This will raise a Signature::AuthenticationError if request does not authenticate
39
- token = request.authenticate do |key|
40
- Signature::Token.new(key, lookup_secret(key))
41
- end
43
+ post '/api/thing' do
44
+ request = Signature::Request.new('POST', env["REQUEST_PATH"], params)
45
+ # This will raise a Signature::AuthenticationError if request does not authenticate
46
+ token = request.authenticate do |key|
47
+ Signature::Token.new(key, lookup_secret(key))
48
+ end
42
49
 
43
- # Do whatever you need to do
44
- end
50
+ # Do whatever you need to do
51
+ end
52
+ ```
45
53
 
46
54
  Developing
47
55
  ----------
@@ -49,6 +57,8 @@ Developing
49
57
  bundle
50
58
  bundle exec rspec spec/*_spec.rb
51
59
 
60
+ Please see the travis status for a list of rubies tested against
61
+
52
62
  Copyright
53
63
  ---------
54
64
 
@@ -1,4 +1,6 @@
1
- require 'hmac-sha2'
1
+ require 'openssl'
2
+
3
+ require 'signature/query_encoder'
2
4
 
3
5
  module Signature
4
6
  class AuthenticationError < RuntimeError; end
@@ -18,6 +20,8 @@ module Signature
18
20
  class Request
19
21
  attr_accessor :path, :query_hash
20
22
 
23
+ include QueryEncoder
24
+
21
25
  # http://www.w3.org/TR/NOTE-datetime
22
26
  ISO8601 = "%Y-%m-%dT%H:%M:%SZ"
23
27
 
@@ -34,63 +38,139 @@ module Signature
34
38
 
35
39
  @method = method.upcase
36
40
  @path, @query_hash, @auth_hash = path, query_hash, auth_hash
41
+ @signed = false
37
42
  end
38
43
 
44
+ # Sign the request with the given token, and return the computed
45
+ # authentication parameters
46
+ #
39
47
  def sign(token)
40
48
  @auth_hash = {
41
49
  :auth_version => "1.0",
42
50
  :auth_key => token.key,
43
51
  :auth_timestamp => Time.now.to_i.to_s
44
52
  }
45
-
46
53
  @auth_hash[:auth_signature] = signature(token)
47
54
 
55
+ @signed = true
56
+
48
57
  return @auth_hash
49
58
  end
50
59
 
51
60
  # Authenticates the request with a token
52
61
  #
53
- # Timestamp check: Unless timestamp_grace is set to nil (which will skip
54
- # the timestamp check), an exception will be raised if timestamp is not
55
- # supplied or if the timestamp provided is not within timestamp_grace of
56
- # the real time (defaults to 10 minutes)
62
+ # Raises an AuthenticationError if the request is invalid.
63
+ # AuthenticationError exception messages are designed to be exposed to API
64
+ # consumers, and should help them correct errors generating signatures
65
+ #
66
+ # Timestamp: Unless timestamp_grace is set to nil (which allows this check
67
+ # to be skipped), AuthenticationError will be raised if the timestamp is
68
+ # missing or further than timestamp_grace period away from the real time
69
+ # (defaults to 10 minutes)
57
70
  #
58
- # Signature check: Raises an exception if the signature does not match the
59
- # computed value
71
+ # Signature: Raises AuthenticationError if the signature does not match
72
+ # the computed HMAC. The error contains a hint for how to sign.
60
73
  #
61
74
  def authenticate_by_token!(token, timestamp_grace = 600)
75
+ # Validate that your code has provided a valid token. This does not
76
+ # raise an AuthenticationError since passing tokens with empty secret is
77
+ # a code error which should be fixed, not reported to the API's consumer
78
+ if token.secret.nil? || token.secret.empty?
79
+ raise "Provided token is missing secret"
80
+ end
81
+
62
82
  validate_version!
63
83
  validate_timestamp!(timestamp_grace)
64
84
  validate_signature!(token)
65
85
  true
66
86
  end
67
87
 
88
+ # Authenticate the request with a token, but rather than raising an
89
+ # exception if the request is invalid, simply returns false
90
+ #
68
91
  def authenticate_by_token(token, timestamp_grace = 600)
69
92
  authenticate_by_token!(token, timestamp_grace)
70
93
  rescue AuthenticationError
71
94
  false
72
95
  end
73
96
 
74
- def authenticate(timestamp_grace = 600, &block)
97
+ # Authenticate a request
98
+ #
99
+ # Takes a block which will be called with the auth_key from the request,
100
+ # and which should return a Signature::Token (or nil if no token can be
101
+ # found for the key)
102
+ #
103
+ # Raises errors in the same way as authenticate_by_token!
104
+ #
105
+ def authenticate(timestamp_grace = 600)
106
+ raise ArgumentError, "Block required" unless block_given?
75
107
  key = @auth_hash['auth_key']
76
- raise AuthenticationError, "Authentication key required" unless key
108
+ raise AuthenticationError, "Missing parameter: auth_key" unless key
77
109
  token = yield key
78
- unless token && token.secret
79
- raise AuthenticationError, "Invalid authentication key"
110
+ unless token
111
+ raise AuthenticationError, "Unknown auth_key"
80
112
  end
81
113
  authenticate_by_token!(token, timestamp_grace)
82
114
  return token
83
115
  end
84
116
 
117
+ # Authenticate a request asynchronously
118
+ #
119
+ # This method is useful it you're running a server inside eventmachine and
120
+ # need to lookup the token asynchronously.
121
+ #
122
+ # The block is passed an auth key and a deferrable which should succeed
123
+ # with the token, or fail if the token cannot be found
124
+ #
125
+ # This method returns a deferrable which succeeds with the valid token, or
126
+ # fails with an AuthenticationError which can be used to pass the error
127
+ # back to the user
128
+ #
129
+ def authenticate_async(timestamp_grace = 600)
130
+ raise ArgumentError, "Block required" unless block_given?
131
+ df = EM::DefaultDeferrable.new
132
+
133
+ key = @auth_hash['auth_key']
134
+
135
+ unless key
136
+ df.fail(AuthenticationError.new("Missing parameter: auth_key"))
137
+ return
138
+ end
139
+
140
+ token_df = yield key
141
+ token_df.callback { |token|
142
+ begin
143
+ authenticate_by_token!(token, timestamp_grace)
144
+ df.succeed(token)
145
+ rescue AuthenticationError => e
146
+ df.fail(e)
147
+ end
148
+ }
149
+ token_df.errback {
150
+ df.fail(AuthenticationError.new("Unknown auth_key"))
151
+ }
152
+ ensure
153
+ return df
154
+ end
155
+
156
+ # Expose the authentication parameters for a signed request
157
+ #
85
158
  def auth_hash
86
- raise "Request not signed" unless @auth_hash && @auth_hash[:auth_signature]
159
+ raise "Request not signed" unless @signed
87
160
  @auth_hash
88
161
  end
89
162
 
163
+ # Query parameters merged with the computed authentication parameters
164
+ #
165
+ def signed_params
166
+ @query_hash.merge(auth_hash)
167
+ end
168
+
90
169
  private
91
170
 
92
171
  def signature(token)
93
- HMAC::SHA256.hexdigest(token.secret, string_to_sign)
172
+ digest = OpenSSL::Digest::SHA256.new
173
+ OpenSSL::HMAC.hexdigest(digest, token.secret, string_to_sign)
94
174
  end
95
175
 
96
176
  def string_to_sign
@@ -106,7 +186,9 @@ module Signature
106
186
  # Exclude signature from signature generation!
107
187
  hash.delete("auth_signature")
108
188
 
109
- hash.keys.sort.map { |k| "#{k}=#{hash[k]}" }.join("&")
189
+ hash.sort.map do |k, v|
190
+ QueryEncoder.encode_param_without_escaping(k, v)
191
+ end.join('&')
110
192
  end
111
193
 
112
194
  def validate_version!
@@ -0,0 +1,47 @@
1
+ module Signature
2
+ # Query string encoding extracted with thanks from em-http-request
3
+ module QueryEncoder
4
+ class << self
5
+ # URL encodes query parameters:
6
+ # single k=v, or a URL encoded array, if v is an array of values
7
+ def encode_param(k, v)
8
+ if v.is_a?(Array)
9
+ v.map { |e| escape(k) + "[]=" + escape(e) }.join("&")
10
+ else
11
+ escape(k) + "=" + escape(v)
12
+ end
13
+ end
14
+
15
+ # Like encode_param, but doesn't url escape keys or values
16
+ def encode_param_without_escaping(k, v)
17
+ if v.is_a?(Array)
18
+ v.map { |e| k + "[]=" + e }.join("&")
19
+ else
20
+ "#{k}=#{v}"
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def escape(s)
27
+ if defined?(EscapeUtils)
28
+ EscapeUtils.escape_url(s.to_s)
29
+ else
30
+ s.to_s.gsub(/([^a-zA-Z0-9_.-]+)/n) {
31
+ '%'+$1.unpack('H2'*bytesize($1)).join('%').upcase
32
+ }
33
+ end
34
+ end
35
+
36
+ if ''.respond_to?(:bytesize)
37
+ def bytesize(string)
38
+ string.bytesize
39
+ end
40
+ else
41
+ def bytesize(string)
42
+ string.size
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end