cloudkit 0.10.1 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (58) hide show
  1. data/CHANGES +11 -0
  2. data/README +7 -6
  3. data/Rakefile +13 -6
  4. data/TODO +7 -5
  5. data/cloudkit.gemspec +23 -23
  6. data/doc/curl.html +2 -2
  7. data/doc/index.html +4 -6
  8. data/examples/5.ru +2 -3
  9. data/examples/TOC +1 -3
  10. data/lib/cloudkit.rb +17 -10
  11. data/lib/cloudkit/constants.rb +0 -6
  12. data/lib/cloudkit/exceptions.rb +10 -0
  13. data/lib/cloudkit/flash_session.rb +1 -3
  14. data/lib/cloudkit/oauth_filter.rb +8 -16
  15. data/lib/cloudkit/oauth_store.rb +9 -13
  16. data/lib/cloudkit/openid_filter.rb +25 -7
  17. data/lib/cloudkit/openid_store.rb +14 -17
  18. data/lib/cloudkit/request.rb +6 -1
  19. data/lib/cloudkit/service.rb +15 -15
  20. data/lib/cloudkit/store.rb +97 -284
  21. data/lib/cloudkit/store/memory_table.rb +105 -0
  22. data/lib/cloudkit/store/resource.rb +256 -0
  23. data/lib/cloudkit/store/response_helpers.rb +0 -1
  24. data/lib/cloudkit/uri.rb +88 -0
  25. data/lib/cloudkit/user_store.rb +7 -14
  26. data/spec/ext_spec.rb +76 -0
  27. data/spec/flash_session_spec.rb +20 -0
  28. data/spec/memory_table_spec.rb +86 -0
  29. data/spec/oauth_filter_spec.rb +326 -0
  30. data/spec/oauth_store_spec.rb +10 -0
  31. data/spec/openid_filter_spec.rb +64 -0
  32. data/spec/openid_store_spec.rb +101 -0
  33. data/spec/rack_builder_spec.rb +39 -0
  34. data/spec/request_spec.rb +185 -0
  35. data/spec/resource_spec.rb +291 -0
  36. data/spec/service_spec.rb +974 -0
  37. data/{test/helper.rb → spec/spec_helper.rb} +14 -2
  38. data/spec/store_spec.rb +10 -0
  39. data/spec/uri_spec.rb +93 -0
  40. data/spec/user_store_spec.rb +10 -0
  41. data/spec/util_spec.rb +11 -0
  42. metadata +37 -61
  43. data/examples/6.ru +0 -10
  44. data/lib/cloudkit/store/adapter.rb +0 -8
  45. data/lib/cloudkit/store/extraction_view.rb +0 -57
  46. data/lib/cloudkit/store/sql_adapter.rb +0 -36
  47. data/test/ext_test.rb +0 -76
  48. data/test/flash_session_test.rb +0 -22
  49. data/test/oauth_filter_test.rb +0 -331
  50. data/test/oauth_store_test.rb +0 -12
  51. data/test/openid_filter_test.rb +0 -60
  52. data/test/openid_store_test.rb +0 -12
  53. data/test/rack_builder_test.rb +0 -41
  54. data/test/request_test.rb +0 -197
  55. data/test/service_test.rb +0 -971
  56. data/test/store_test.rb +0 -93
  57. data/test/user_store_test.rb +0 -12
  58. data/test/util_test.rb +0 -13
@@ -1,22 +0,0 @@
1
- require 'helper'
2
- class FlashSessionTest < Test::Unit::TestCase
3
-
4
- context "A FlashSession" do
5
-
6
- setup do
7
- @flash = CloudKit::FlashSession.new
8
- end
9
-
10
- should "accept a value for a key" do
11
- @flash['greeting'] = 'hello'
12
- assert_equal 'hello', @flash['greeting']
13
- end
14
-
15
- should "erase a key/value pair after access" do
16
- @flash['greeting'] = 'hello'
17
- x = @flash['greeting']
18
- assert_nil @flash['greeting']
19
- end
20
-
21
- end
22
- end
@@ -1,331 +0,0 @@
1
- require 'helper'
2
- class OAuthFilterTest < Test::Unit::TestCase
3
-
4
- context "An OAuthFilter" do
5
-
6
- setup do
7
- @oauth_filtered_app = CloudKit::OAuthFilter.new(echo_env(CLOUDKIT_AUTH_KEY))
8
- token = JSON.generate(
9
- :secret => 'pfkkdhi9sl3r4s00',
10
- :consumer_key => 'dpf43f3p2l4k3l03',
11
- :consumer_secret => 'kd94hf93k423kf44',
12
- :user_id => 'martino')
13
- Rack::MockRequest.new(@oauth_filtered_app).get('/') # prime the storage
14
- @store = @oauth_filtered_app.store
15
- result = @store.put('/cloudkit_oauth_tokens/nnch734d00sl2jdk', :json => token)
16
- @token_etag = result.parsed_content['etag']
17
- end
18
-
19
- teardown do
20
- @store.reset!
21
- @store.load_static_consumer
22
- end
23
-
24
- should "verify signatures" do
25
- response = do_get
26
- assert_equal 200, response.status
27
- assert_equal 'martino', response.body
28
- end
29
-
30
- should "notify downstream nodes of its presence" do
31
- app = CloudKit::OAuthFilter.new(echo_env(CLOUDKIT_VIA))
32
- response = Rack::MockRequest.new(app).get('/')
33
- assert_equal CLOUDKIT_OAUTH_FILTER_KEY, response.body
34
- end
35
-
36
- should "not allow a nonce/timestamp combination to appear twice" do
37
- do_get
38
- response = do_get
39
- assert_equal '', response.body
40
- get_request_token
41
- response = get_request_token
42
- assert_equal 401, response.status
43
- end
44
-
45
- should "add the remote user to the rack environment for verified requests" do
46
- response = do_get
47
- assert_equal 'martino', response.body
48
- end
49
-
50
- should "allow requests for / to pass through" do
51
- response = Rack::MockRequest.new(@oauth_filtered_app).get('/')
52
- assert_equal 200, response.status
53
- end
54
-
55
- should "reject unauthorized requests" do
56
- response = Rack::MockRequest.new(@oauth_filtered_app).get(
57
- 'http://photos.example.net/photos?file=vacation.jpg&size=original' +
58
- '&oauth_version=1.0' +
59
- '&oauth_consumer_key=dpf43f3p2l4k3l03' +
60
- '&oauth_token=nnch734d00sl2jdk' +
61
- '&oauth_timestamp=1191242096' +
62
- '&oauth_nonce=kllo9940pd9333jh' +
63
- '&oauth_signature=fail'+
64
- '&oauth_signature_method=HMAC-SHA1', 'X-Remote-User' => 'intruder') # TODO rework
65
- assert_equal '', response.body
66
- end
67
-
68
- context "supporting OAuth Discovery" do
69
-
70
- should "set the auth challenge for unauthorized requests" do
71
- app = CloudKit::OAuthFilter.new(
72
- lambda {|env| [200, {}, [env[CLOUDKIT_AUTH_CHALLENGE]['WWW-Authenticate'] || '']]})
73
- response = Rack::MockRequest.new(app).get(
74
- '/items', 'HTTP_HOST' => 'example.org')
75
- assert_equal 'OAuth realm="http://example.org"', response.body
76
- app = CloudKit::OAuthFilter.new(
77
- lambda {|env| [200, {}, [env[CLOUDKIT_AUTH_CHALLENGE]['Link'] || '']]})
78
- response = Rack::MockRequest.new(app).get(
79
- '/items', 'HTTP_HOST' => 'example.org')
80
- assert_equal '<http://example.org/oauth/meta>; rel="http://oauth.net/discovery/1.0/rel/provider"',
81
- response.body
82
- end
83
-
84
- should "provide XRD metadata on GET /oauth/meta" do
85
- response = Rack::MockRequest.new(@oauth_filtered_app).get(
86
- '/oauth/meta', 'HTTP_HOST' => 'example.org')
87
- assert_equal 200, response.status
88
- doc = REXML::Document.new(response.body)
89
- assert REXML::XPath.first(doc, '//XRD/Type')
90
- assert_equal 'http://oauth.net/discovery/1.0',
91
- REXML::XPath.first(doc, '//XRD/Type').children[0].to_s
92
- assert REXML::XPath.first(doc, '//XRD/Service/Type')
93
- assert_equal 'http://oauth.net/discovery/1.0/rel/provider',
94
- REXML::XPath.first(doc, '//XRD/Service/Type').children[0].to_s
95
- assert REXML::XPath.first(doc, '//XRD/Service/URI')
96
- assert_equal 'http://example.org/oauth',
97
- REXML::XPath.first(doc, '//XRD/Service/URI').children[0].to_s
98
- end
99
-
100
- should "respond to OAuth Discovery Draft 2 / XRDS-Simple Discovery" do
101
- response = Rack::MockRequest.new(@oauth_filtered_app).get(
102
- '/anything',
103
- 'HTTP_HOST' => 'example.org',
104
- 'HTTP_ACCEPT' => 'application/xrds+xml')
105
- assert 200, response.status
106
- assert_equal 'http://example.org/oauth', response['X-XRDS-Location']
107
- end
108
-
109
- should "provide a descriptor document on GET /oauth" do
110
- response = Rack::MockRequest.new(@oauth_filtered_app).get(
111
- '/oauth', 'HTTP_HOST' => 'example.org')
112
- assert_equal 200, response.status
113
- assert_equal 'application/xrds+xml', response['Content-Type']
114
- end
115
-
116
- should "populate the static consumer on startup" do
117
- response = @store.get('/cloudkit_oauth_consumers/cloudkitconsumer')
118
- assert_equal 200, response.status
119
- end
120
-
121
- end
122
-
123
- context "supporting authorization" do
124
-
125
- should "generate request tokens" do
126
- response = get_request_token
127
- assert_equal 201, response.status
128
- token, secret = response.body.split('&')
129
- token_parts = token.split('=')
130
- secret_parts = secret.split('=')
131
- assert_equal 'oauth_token', token_parts.first
132
- assert_equal 'oauth_token_secret', secret_parts.first
133
- assert token_parts.last
134
- assert !token_parts.last.empty?
135
- assert secret_parts.last
136
- assert !secret_parts.last.empty?
137
- end
138
-
139
- should "not generate request tokens for invalid consumers" do
140
- # this does not mean consumers must register, only that they
141
- # should use the static value provided in the xrd document
142
- # or one that is specified in the consumer database
143
- pre_sign = Rack::Request.new(Rack::MockRequest.env_for(
144
- 'http://photos.example.net/oauth/request_tokens',
145
- 'Authorization' => 'OAuth realm="http://photos.example.net", ' +
146
- 'oauth_version="1.0", ' +
147
- 'oauth_consumer_key="mysteryconsumer", ' +
148
- 'oauth_timestamp="1191242096", ' +
149
- 'oauth_nonce="AAAAAAAAAAAAAAAAA", ' +
150
- 'oauth_signature_method="HMAC-SHA1"',
151
- :method => "POST"))
152
- signature = OAuth::Signature.build(pre_sign) do |token, consumer_key|
153
- [nil, '']
154
- end
155
- response = Rack::MockRequest.new(@oauth_filtered_app).post(
156
- 'http://photos.example.net/oauth/request_tokens?' +
157
- 'oauth_version=1.0' +
158
- '&oauth_consumer_key=mysteryconsumer' +
159
- '&oauth_timestamp=1191242096' +
160
- '&oauth_nonce=AAAAAAAAAAAAAAAAA' +
161
- '&oauth_signature=' + CGI.escape(signature.signature) +
162
- '&oauth_signature_method=HMAC-SHA1')
163
- assert_equal 401, response.status
164
- end
165
-
166
- should "store request tokens for authorizaton" do
167
- response = get_request_token
168
- assert_equal 201, response.status
169
- token, secret = extract_token(response)
170
- request_token = @store.get("/cloudkit_oauth_request_tokens/#{token}").parsed_content
171
- assert request_token
172
- assert_equal secret, request_token['secret']
173
- assert !request_token['authorized_at']
174
- end
175
-
176
- should "redirect to login before allowing GET requests for request token authorization" do
177
- response = get_request_token
178
- token, secret = extract_token(response)
179
- response = Rack::MockRequest.new(@oauth_filtered_app).get(
180
- "/oauth/authorization?oauth_token=#{token}")
181
- assert_equal 302, response.status
182
- assert_equal '/login', response['Location']
183
- end
184
-
185
- should "respond successfully to authorization GET requests for logged-in users with a valid request token" do
186
- response = get_request_token
187
- token, secret = extract_token(response)
188
- response = Rack::MockRequest.new(@oauth_filtered_app).get(
189
- "/oauth/authorization?oauth_token=#{token}", VALID_TEST_AUTH)
190
- assert_equal 200, response.status
191
- end
192
-
193
- should "reject authorization GET requests with invalid tokens" do
194
- response = get_request_token
195
- token, secret = extract_token(response)
196
- response = Rack::MockRequest.new(@oauth_filtered_app).get(
197
- "/oauth/authorization?oauth_token=fail", VALID_TEST_AUTH)
198
- assert_equal 401, response.status
199
- end
200
-
201
- should "authorize request tokens for verified requests" do
202
- response = get_request_token
203
- token, secret = extract_token(response)
204
- response = Rack::MockRequest.new(@oauth_filtered_app).put(
205
- "/oauth/authorized_request_tokens/#{token}?submit=Approve", VALID_TEST_AUTH)
206
- assert_equal 200, response.status
207
- request_token = @store.get("/cloudkit_oauth_request_tokens/#{token}").parsed_content
208
- assert request_token['authorized_at']
209
- assert request_token['user_id']
210
- end
211
-
212
- should "removed denied request tokens" do
213
- response = get_request_token
214
- token, secret = extract_token(response)
215
- response = Rack::MockRequest.new(@oauth_filtered_app).put(
216
- "/oauth/authorized_request_tokens/#{token}?submit=Deny", VALID_TEST_AUTH)
217
- assert_equal 200, response.status
218
- request_token = @store.get("/cloudkit_oauth_request_tokens/#{token}").parsed_content
219
- assert 410, response.status
220
- end
221
-
222
- should "redirect to login for authorization PUT requests unless logged-in" do
223
- response = get_request_token
224
- token, secret = extract_token(response)
225
- response = Rack::MockRequest.new(@oauth_filtered_app).put(
226
- "/oauth/authorized_request_tokens/#{token}?submit=Approve")
227
- assert_equal 302, response.status
228
- assert_equal '/login', response['Location']
229
- end
230
-
231
- should "not create access tokens for request tokens that have already been authorized" do
232
- response = get_request_token
233
- token, secret = extract_token(response)
234
- response = Rack::MockRequest.new(@oauth_filtered_app).put(
235
- "/oauth/authorized_request_tokens/#{token}?submit=Approve", VALID_TEST_AUTH)
236
- assert_equal 200, response.status
237
- response = Rack::MockRequest.new(@oauth_filtered_app).put(
238
- "/oauth/authorized_request_tokens/#{token}?submit=Approve", VALID_TEST_AUTH)
239
- assert_equal 401, response.status
240
- end
241
-
242
- should "provide access tokens in exchange for authorized request tokens" do
243
- response = get_access_token
244
- assert_equal 201, response.status
245
- token, secret = extract_token(response)
246
- assert !token.empty?
247
- assert !secret.empty?
248
- end
249
-
250
- should "remove request tokens after creating access tokens" do
251
- response = get_access_token
252
- assert_equal 201, response.status
253
- request_tokens = @store.get('/cloudkit_oauth_request_tokens').parsed_content
254
- assert_equal 0, request_tokens['uris'].size
255
- end
256
-
257
- end
258
- end
259
-
260
- def do_get
261
- Rack::MockRequest.new(@oauth_filtered_app).get(
262
- 'http://photos.example.net/photos?file=vacation.jpg&size=original' +
263
- '&oauth_version=1.0' +
264
- '&oauth_consumer_key=dpf43f3p2l4k3l03' +
265
- '&oauth_token=nnch734d00sl2jdk' +
266
- '&oauth_timestamp=1191242096' +
267
- '&oauth_nonce=kllo9940pd9333jh' +
268
- '&oauth_signature=tR3%2BTy81lMeYAr%2FFid0kMTYa%2FWM%3D' +
269
- '&oauth_signature_method=HMAC-SHA1')
270
- end
271
-
272
- def get_request_token
273
- pre_sign = Rack::Request.new(Rack::MockRequest.env_for(
274
- 'http://photos.example.net/oauth/request_tokens',
275
- 'Authorization' => 'OAuth realm="http://photos.example.net", ' +
276
- 'oauth_version="1.0", ' +
277
- 'oauth_consumer_key="cloudkitconsumer", ' +
278
- 'oauth_timestamp="1191242096", ' +
279
- 'oauth_nonce="AAAAAAAAAAAAAAAAA", ' +
280
- 'oauth_signature_method="HMAC-SHA1"',
281
- :method => "POST"))
282
- signature = OAuth::Signature.build(pre_sign) do |token, consumer_key|
283
- [nil, '']
284
- end
285
- Rack::MockRequest.new(@oauth_filtered_app).post(
286
- 'http://photos.example.net/oauth/request_tokens?' +
287
- 'oauth_version=1.0' +
288
- '&oauth_consumer_key=cloudkitconsumer' +
289
- '&oauth_timestamp=1191242096' +
290
- '&oauth_nonce=AAAAAAAAAAAAAAAAA' +
291
- '&oauth_signature=' + CGI.escape(signature.signature) +
292
- '&oauth_signature_method=HMAC-SHA1')
293
- end
294
-
295
- def get_access_token
296
- response = get_request_token
297
- token, secret = extract_token(response)
298
- response = Rack::MockRequest.new(@oauth_filtered_app).put(
299
- "/oauth/authorized_request_tokens/#{token}", VALID_TEST_AUTH)
300
- assert_equal 200, response.status
301
- pre_sign = Rack::Request.new(Rack::MockRequest.env_for(
302
- 'http://photos.example.net/oauth/access_tokens',
303
- 'Authorization' => 'OAuth realm="http://photos.example.net", ' +
304
- 'oauth_version="1.0", ' +
305
- 'oauth_consumer_key="cloudkitconsumer", ' +
306
- 'oauth_token="' + token + '", ' +
307
- 'oauth_timestamp="1191242097", ' +
308
- 'oauth_nonce="AAAAAAAAAAAAAAAAA", ' +
309
- 'oauth_signature_method="HMAC-SHA1"',
310
- :method => "POST"))
311
- signature = OAuth::Signature.build(pre_sign) do |token, consumer_key|
312
- [secret, '']
313
- end
314
- Rack::MockRequest.new(@oauth_filtered_app).post(
315
- 'http://photos.example.net/oauth/access_tokens?' +
316
- 'oauth_version=1.0' +
317
- '&oauth_consumer_key=cloudkitconsumer' +
318
- '&oauth_token=' + token +
319
- '&oauth_timestamp=1191242097' +
320
- '&oauth_nonce=AAAAAAAAAAAAAAAAA' +
321
- '&oauth_signature=' + CGI.escape(signature.signature) +
322
- '&oauth_signature_method=HMAC-SHA1')
323
- end
324
-
325
- def extract_token(response)
326
- token, secret = response.body.split('&')
327
- token_parts = token.split('=')
328
- secret_parts = secret.split('=')
329
- return token_parts.last, secret_parts.last
330
- end
331
- end
@@ -1,12 +0,0 @@
1
- require 'helper'
2
- class OAuthStoreTest < Test::Unit::TestCase
3
-
4
- context "An OAuthStore" do
5
-
6
- should "know its version" do
7
- store = CloudKit::UserStore.new
8
- assert_equal 1, store.version
9
- end
10
-
11
- end
12
- end
@@ -1,60 +0,0 @@
1
- require 'helper'
2
- class OpenIDFilterTest < Test::Unit::TestCase
3
-
4
- context "An OpenIDFilter" do
5
-
6
- setup do
7
- openid_app = Rack::Builder.new {
8
- use Rack::Lint
9
- use Rack::Session::Pool
10
- use CloudKit::OpenIDFilter
11
- run echo_env(CLOUDKIT_AUTH_KEY)
12
- }
13
- @request = Rack::MockRequest.new(openid_app)
14
- end
15
-
16
- should "allow root url pass through" do
17
- response = @request.get('/')
18
- assert_equal 200, response.status
19
- end
20
-
21
- should "redirect to the login page if authorization is required" do
22
- response = @request.get('/protected')
23
- assert_equal 302, response.status
24
- assert_equal '/login', response['Location']
25
- end
26
-
27
- should "notify downstream nodes of its presence" do
28
- app = Rack::Builder.new do
29
- use Rack::Session::Pool
30
- use CloudKit::OpenIDFilter
31
- run echo_env(CLOUDKIT_VIA)
32
- end
33
- response = Rack::MockRequest.new(app).get('/')
34
- assert_equal CLOUDKIT_OPENID_FILTER_KEY, response.body
35
- end
36
-
37
- context "with upstream authorization middleware" do
38
-
39
- should "allow pass through if the auth env variable is populated" do
40
- response = @request.get('/protected', VALID_TEST_AUTH)
41
- assert_equal 200, response.status
42
- assert_equal TEST_REMOTE_USER, response.body
43
- end
44
-
45
- should "return the auth challenge header" do
46
- response = @request.get('/protected',
47
- CLOUDKIT_VIA => CLOUDKIT_OAUTH_FILTER_KEY,
48
- CLOUDKIT_AUTH_CHALLENGE => {'WWW-Authenticate' => 'etc.'})
49
- assert response['WWW-Authenticate']
50
- end
51
-
52
- should "return a 401 status if authorization is required" do
53
- response = @request.get('/protected',
54
- CLOUDKIT_VIA => CLOUDKIT_OAUTH_FILTER_KEY,
55
- CLOUDKIT_AUTH_CHALLENGE => {'WWW-Authenticate' => 'etc.'})
56
- assert_equal 401, response.status
57
- end
58
- end
59
- end
60
- end
@@ -1,12 +0,0 @@
1
- require 'helper'
2
- class OpenIDStoreTest < Test::Unit::TestCase
3
-
4
- context "An OpenIDStore" do
5
-
6
- should "know its version" do
7
- store = CloudKit::UserStore.new
8
- assert_equal 1, store.version
9
- end
10
-
11
- end
12
- end
@@ -1,41 +0,0 @@
1
- require 'helper'
2
- class RackBuilderTest < Test::Unit::TestCase
3
-
4
- context "Rack::Builder" do
5
-
6
- should "expose services" do
7
- app = Rack::Builder.new do
8
- expose :items, :things
9
- run lambda {|app| [200, {}, ['hello']]}
10
- end
11
- response = Rack::MockRequest.new(app).get('/items')
12
- assert_equal 200, response.status
13
- documents = JSON.parse(response.body)['uris']
14
- assert_equal [], documents
15
- end
16
-
17
- should "expose services with auth using 'contain'" do
18
- app = Rack::Builder.new do
19
- contain :items, :things
20
- run lambda {|app| [200, {}, ['hello']]}
21
- end
22
- response = Rack::MockRequest.new(app).get('/items')
23
- assert_equal 401, response.status
24
- response = Rack::MockRequest.new(app).get('/things')
25
- assert_equal 401, response.status
26
- response = Rack::MockRequest.new(app).get('/')
27
- assert_equal 200, response.status
28
- assert_equal 'hello', response.body
29
- end
30
-
31
- should "insert a default app if one does not exist" do
32
- app = Rack::Builder.new { contain :items }
33
- response = Rack::MockRequest.new(app).get('/items')
34
- assert_equal 401, response.status
35
- response = Rack::MockRequest.new(app).get('/')
36
- assert_equal 200, response.status
37
- assert response.body.match('CloudKit')
38
- end
39
-
40
- end
41
- end