strelka 0.0.1.pre.244 → 0.0.1.pre.252

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.
Files changed (40) hide show
  1. data.tar.gz.sig +0 -0
  2. data/ChangeLog +57 -3
  3. data/Manifest.txt +3 -4
  4. data/Rakefile +1 -1
  5. data/bin/strelka +4 -7
  6. data/examples/.env +4 -0
  7. data/examples/Procfile +8 -0
  8. data/examples/apps/auth-demo +3 -5
  9. data/examples/apps/auth-demo2 +10 -9
  10. data/{data/strelka → examples}/apps/hello-world +1 -2
  11. data/examples/apps/sessions-demo +2 -2
  12. data/examples/config.yml +12 -1
  13. data/examples/gen-config.rb +5 -6
  14. data/examples/templates/auth-form.tmpl +4 -0
  15. data/examples/templates/auth-success.tmpl +3 -1
  16. data/lib/strelka.rb +15 -21
  17. data/lib/strelka/app.rb +53 -31
  18. data/lib/strelka/app/auth.rb +260 -132
  19. data/lib/strelka/app/sessions.rb +25 -31
  20. data/lib/strelka/authprovider/basic.rb +33 -9
  21. data/lib/strelka/authprovider/hostaccess.rb +1 -1
  22. data/lib/strelka/constants.rb +0 -11
  23. data/lib/strelka/cookie.rb +17 -1
  24. data/lib/strelka/httpresponse/negotiation.rb +1 -1
  25. data/lib/strelka/session/db.rb +49 -25
  26. data/lib/strelka/session/default.rb +39 -19
  27. data/spec/lib/helpers.rb +3 -24
  28. data/spec/strelka/app/auth_spec.rb +461 -177
  29. data/spec/strelka/app/sessions_spec.rb +7 -26
  30. data/spec/strelka/app_spec.rb +3 -3
  31. data/spec/strelka/authprovider/basic_spec.rb +4 -4
  32. data/spec/strelka/httprequest/session_spec.rb +1 -1
  33. data/spec/strelka/httpresponse/session_spec.rb +1 -1
  34. data/spec/strelka/session/db_spec.rb +6 -1
  35. data/spec/strelka/session/default_spec.rb +3 -3
  36. metadata +7 -8
  37. metadata.gz.sig +2 -2
  38. data/examples/apps/ws-echo +0 -17
  39. data/lib/strelka/logging.rb +0 -301
  40. data/spec/strelka/logging_spec.rb +0 -74
data/spec/lib/helpers.rb CHANGED
@@ -28,6 +28,7 @@ if ENV['COVERAGE']
28
28
  end
29
29
 
30
30
  require 'loggability'
31
+ require 'loggability/spechelpers'
31
32
  require 'configurability'
32
33
  require 'pathname'
33
34
  require 'tmpdir'
@@ -55,30 +56,6 @@ module Strelka::SpecHelpers
55
56
  end
56
57
 
57
58
 
58
- ### Reset the logging subsystem to its default state.
59
- def reset_logging
60
- Loggability.formatter = nil
61
- Loggability.output_to( $stderr )
62
- Loggability.level = :fatal
63
- end
64
-
65
-
66
- ### Alter the output of the default log formatter to be pretty in SpecMate output
67
- def setup_logging( level=:fatal )
68
-
69
- # Only do this when executing from a spec in TextMate
70
- if ENV['HTML_LOGGING'] || (ENV['TM_FILENAME'] && ENV['TM_FILENAME'] =~ /_spec\.rb/)
71
- logarray = []
72
- Thread.current['logger-output'] = logarray
73
- Loggability.output_to( logarray )
74
- Loggability.format_as( :html )
75
- Loggability.level = :debug
76
- else
77
- Loggability.level = level
78
- end
79
- end
80
-
81
-
82
59
  ### Set up a Mongrel2 configuration database according to the specified +dbspec+.
83
60
  ### Set up a Mongrel2 configuration database in memory.
84
61
  def setup_config_db
@@ -272,8 +249,10 @@ RSpec.configure do |c|
272
249
 
273
250
  c.mock_with( :rspec )
274
251
 
252
+ c.extend( Strelka::TestConstants )
275
253
  c.extend( Strelka::TestConstants )
276
254
  c.include( Strelka::TestConstants )
255
+ c.include( Loggability::SpecHelpers )
277
256
  c.include( Mongrel2::SpecHelpers )
278
257
  c.include( Strelka::SpecHelpers )
279
258
  # c.include( Strelka::Matchers )
@@ -89,12 +89,12 @@ describe Strelka::App::Auth do
89
89
  end
90
90
 
91
91
 
92
- it "applies auth to every request by default" do
92
+ it "applies authentication and authorization to every request by default" do
93
93
  app = @app.new
94
94
  req = @request_factory.get( '/api/v1' )
95
95
 
96
96
  app.auth_provider.should_receive( :authenticate ).and_return( 'anonymous' )
97
- app.auth_provider.should_receive( :authorize ).and_return( true )
97
+ app.auth_provider.should_receive( :authorize )
98
98
 
99
99
  res = app.handle( req )
100
100
 
@@ -117,300 +117,584 @@ describe Strelka::App::Auth do
117
117
  end
118
118
 
119
119
  it "has its auth config inherited by subclasses" do
120
- @app.class_eval do
121
- auth_provider :hostaccess
122
- authz_callback { true }
123
- end
124
120
  subclass = Class.new( @app )
125
121
 
126
- subclass.auth_provider.should == @app.auth_provider
127
- subclass.authz_callback.should == @app.authz_callback
128
122
  subclass.positive_auth_criteria.should == @app.positive_auth_criteria
129
123
  subclass.positive_auth_criteria.should_not equal( @app.positive_auth_criteria )
130
124
  subclass.negative_auth_criteria.should == @app.negative_auth_criteria
131
125
  subclass.negative_auth_criteria.should_not equal( @app.negative_auth_criteria )
126
+ subclass.positive_perms_criteria.should == @app.positive_perms_criteria
127
+ subclass.positive_perms_criteria.should_not equal( @app.positive_perms_criteria )
128
+ subclass.negative_perms_criteria.should == @app.negative_perms_criteria
129
+ subclass.negative_perms_criteria.should_not equal( @app.negative_perms_criteria )
132
130
  end
133
131
 
134
132
 
135
- context "that has negative auth criteria for the root" do
133
+ it "allows auth criteria to be declared with a string" do
134
+ @app.require_auth_for( '/string' )
135
+ app = @app.new
136
136
 
137
- before( :each ) do
138
- @app.no_auth_for( '/' )
139
- end
137
+ req = @request_factory.get( '/api/v1/string' )
138
+ app.request_should_auth?( req ).should be_true()
139
+ req = @request_factory.get( '/api/v1/strong' )
140
+ app.request_should_auth?( req ).should be_false()
141
+ req = @request_factory.get( '/api/v1/stri' )
142
+ app.request_should_auth?( req ).should be_false()
143
+ req = @request_factory.get( '/api/v1/string/long' )
144
+ app.request_should_auth?( req ).should be_false()
145
+ end
140
146
 
141
- it "knows that it has auth criteria" do
142
- @app.should have_auth_criteria()
147
+ it "allows auth criteria to be declared with a regexp" do
148
+ @app.require_auth_for( %r{/str[io]} )
149
+ app = @app.new
150
+
151
+ req = @request_factory.get( '/api/v1/stri' )
152
+ app.request_should_auth?( req ).should be_true()
153
+ req = @request_factory.get( '/api/v1/stro' )
154
+ app.request_should_auth?( req ).should be_true()
155
+ req = @request_factory.get( '/api/v1/string' ) # not right-bound
156
+ app.request_should_auth?( req ).should be_true()
157
+ req = @request_factory.get( '/api/v1/string/long' )
158
+ app.request_should_auth?( req ).should be_true()
159
+ req = @request_factory.get( '/api/v1/other/string/long' ) # Not left-bound
160
+ app.request_should_auth?( req ).should be_true()
161
+ req = @request_factory.get( '/api/v1/chatlog' ) # Not left-bound
162
+ app.request_should_auth?( req ).should be_false()
163
+ end
164
+
165
+ it "allows auth criteria to be declared with a string and a block" do
166
+ @app.require_auth_for( 'string' ) do |req|
167
+ req.verb != :GET
143
168
  end
144
169
 
145
- it "doesn't pass a request for the root path through auth" do
146
- req = @request_factory.get( '/api/v1/' )
170
+ app = @app.new
147
171
 
148
- app = @app.new
149
- app.auth_provider.should_not_receive( :authenticate )
150
- app.auth_provider.should_not_receive( :authorize )
172
+ req = @request_factory.get( '/api/v1/string' )
173
+ app.request_should_auth?( req ).should be_false()
174
+ req = @request_factory.post( '/api/v1/string' )
175
+ app.request_should_auth?( req ).should be_true()
176
+ req = @request_factory.put( '/api/v1/string' )
177
+ app.request_should_auth?( req ).should be_true()
178
+ req = @request_factory.delete( '/api/v1/string' )
179
+ app.request_should_auth?( req ).should be_true()
180
+ req = @request_factory.options( '/api/v1/string' )
181
+ app.request_should_auth?( req ).should be_true()
182
+ end
151
183
 
152
- app.handle( req )
184
+ it "allows auth criteria to be declared with a regexp and a block" do
185
+ @app.require_auth_for( %r{/regexp(?:/(?<username>\w+))?} ) do |req, match|
186
+ match[:username] ? true : false
153
187
  end
154
188
 
155
- it "passes a request for a path other than the root through auth" do
156
- req = @request_factory.get( '/api/v1/console' )
189
+ app = @app.new
157
190
 
158
- app = @app.new
159
- app.auth_provider.should_receive( :authenticate ).and_return( 'anonymous' )
160
- app.auth_provider.should_receive( :authorize ).and_return( true )
191
+ req = @request_factory.get( '/api/v1/regexp' )
192
+ app.request_should_auth?( req ).should be_false()
193
+ req = @request_factory.get( '/api/v1/regexp/a_username' )
194
+ app.request_should_auth?( req ).should be_true()
195
+ req = @request_factory.get( '/api/v1/regexp/%20not+a+username' )
196
+ app.request_should_auth?( req ).should be_false()
197
+ end
161
198
 
162
- app.handle( req )
199
+ it "allows auth criteria to be declared with just a block" do
200
+ @app.require_auth_for do |req|
201
+ path = req.app_path.gsub( %r{^/+|/+$}, '' )
202
+
203
+ (
204
+ path == 'strong' or
205
+ path =~ %r{^marlon_brando$}i or
206
+ req.verb == :POST or
207
+ req.content_type == 'application/x-www-form-urlencoded'
208
+ )
163
209
  end
164
210
 
211
+ app = @app.new
212
+
213
+ req = @request_factory.get( '/api/v1/strong' )
214
+ app.request_should_auth?( req ).should be_true()
215
+ req = @request_factory.get( '/api/v1/marlon_brando' )
216
+ app.request_should_auth?( req ).should be_true()
217
+ req = @request_factory.post( '/api/v1/somewhere' )
218
+ app.request_should_auth?( req ).should be_true()
219
+ req = @request_factory.put( '/api/v1/somewhere' )
220
+ req.content_type = 'application/x-www-form-urlencoded'
221
+ app.request_should_auth?( req ).should be_true()
222
+
223
+ req = @request_factory.get( '/api/v1/string' )
224
+ app.request_should_auth?( req ).should be_false()
225
+ req = @request_factory.get( '/api/v1/marlon_brando/2' )
226
+ app.request_should_auth?( req ).should be_false()
227
+ req = @request_factory.put( '/api/v1/somewhere' )
228
+ app.request_should_auth?( req ).should be_false()
229
+
165
230
  end
166
231
 
167
- context "that has negative auth criteria" do
168
232
 
169
- before( :each ) do
170
- @app.no_auth_for( '/login' )
171
- end
233
+ it "allows negative auth criteria to be declared with a string" do
234
+ @app.no_auth_for( '/string' )
235
+ app = @app.new
172
236
 
173
- it "knows that it has auth criteria" do
174
- @app.should have_auth_criteria()
175
- end
237
+ req = @request_factory.get( '/api/v1/string' )
238
+ app.request_should_auth?( req ).should be_false()
239
+ req = @request_factory.get( '/api/v1/strong' )
240
+ app.request_should_auth?( req ).should be_true()
241
+ req = @request_factory.get( '/api/v1/stri' )
242
+ app.request_should_auth?( req ).should be_true()
243
+ req = @request_factory.get( '/api/v1/string/long' )
244
+ app.request_should_auth?( req ).should be_true()
245
+ end
176
246
 
177
- it "doesn't pass a request that matches through auth" do
178
- req = @request_factory.get( '/api/v1/login' )
247
+ it "allows negative auth criteria to be declared with a regexp" do
248
+ @app.no_auth_for( %r{/str[io]} )
249
+ app = @app.new
179
250
 
180
- app = @app.new
181
- app.auth_provider.should_not_receive( :authenticate )
182
- app.auth_provider.should_not_receive( :authorize )
251
+ req = @request_factory.get( '/api/v1/stri' )
252
+ app.request_should_auth?( req ).should be_false()
253
+ req = @request_factory.get( '/api/v1/stro' )
254
+ app.request_should_auth?( req ).should be_false()
255
+ req = @request_factory.get( '/api/v1/string' ) # not right-bound
256
+ app.request_should_auth?( req ).should be_false()
257
+ req = @request_factory.get( '/api/v1/string/long' )
258
+ app.request_should_auth?( req ).should be_false()
259
+ req = @request_factory.get( '/api/v1/other/string/long' ) # Not left-bound
260
+ app.request_should_auth?( req ).should be_false()
261
+ req = @request_factory.get( '/api/v1/chat' )
262
+ app.request_should_auth?( req ).should be_true()
263
+ end
183
264
 
184
- app.handle( req )
265
+ it "allows negative auth criteria to be declared with a string and a block" do
266
+ @app.no_auth_for( 'string' ) do |req|
267
+ Strelka.log.debug "Checking request verb: %p" % [ req.verb ]
268
+ req.verb == :GET
185
269
  end
186
270
 
187
- it "passes a request that doesn't match through auth" do
188
- req = @request_factory.get( '/api/v1/console' )
271
+ app = @app.new
189
272
 
190
- app = @app.new
191
- app.auth_provider.should_receive( :authenticate ).and_return( 'anonymous' )
192
- app.auth_provider.should_receive( :authorize ).and_return( true )
273
+ req = @request_factory.get( '/api/v1/string' )
274
+ app.request_should_auth?( req ).should be_false()
275
+ req = @request_factory.get( '/api/v1/strong' )
276
+ app.request_should_auth?( req ).should be_true()
277
+ req = @request_factory.post( '/api/v1/string' )
278
+ app.request_should_auth?( req ).should be_true()
279
+ req = @request_factory.put( '/api/v1/string' )
280
+ app.request_should_auth?( req ).should be_true()
281
+ req = @request_factory.delete( '/api/v1/string' )
282
+ app.request_should_auth?( req ).should be_true()
283
+ req = @request_factory.options( '/api/v1/string' )
284
+ app.request_should_auth?( req ).should be_true()
285
+ end
193
286
 
194
- app.handle( req )
287
+ it "allows negative auth criteria to be declared with a regexp and a block" do
288
+ @app.no_auth_for( %r{/regexp(?:/(?<username>\w+))?} ) do |req, match|
289
+ match[:username] == 'guest'
195
290
  end
196
291
 
197
- end
292
+ app = @app.new
198
293
 
199
- context "that has a negative auth criteria block" do
294
+ req = @request_factory.get( '/api/v1/regexp' )
295
+ app.request_should_auth?( req ).should be_true()
296
+ req = @request_factory.get( '/api/v1/regexp/a_username' )
297
+ app.request_should_auth?( req ).should be_true()
298
+ req = @request_factory.get( '/api/v1/regexp/%20not+a+username' )
299
+ app.request_should_auth?( req ).should be_true()
300
+ req = @request_factory.get( '/api/v1/regexp/guest' )
301
+ app.request_should_auth?( req ).should be_false()
302
+ end
200
303
 
201
- before( :each ) do
202
- @app.no_auth_for do |req|
203
- req.notes[:skip_auth]
204
- end
304
+ it "allows negative auth criteria to be declared with just a block" do
305
+ @app.no_auth_for do |req|
306
+ req.app_path == '/foom' &&
307
+ req.verb == :GET &&
308
+ req.headers.accept.include?( 'text/plain' )
205
309
  end
206
310
 
207
- it "knows that it has auth criteria" do
208
- @app.should have_auth_criteria()
209
- end
311
+ app = @app.new
210
312
 
211
- it "doesn't pass a request for which the block returns true through auth" do
212
- req = @request_factory.get( '/api/v1/login' )
213
- req.notes[:skip_auth] = true
313
+ req = @request_factory.get( '/api/v1/foom' )
314
+ app.request_should_auth?( req ).should be_true()
315
+ req = @request_factory.post( '/api/v1/foom', :accept => 'text/plain, text/html; q=0.5' )
316
+ app.request_should_auth?( req ).should be_true()
317
+ req = @request_factory.get( '/api/v1/foom', :accept => 'text/plain, text/html; q=0.5' )
318
+ app.request_should_auth?( req ).should be_false()
214
319
 
215
- app = @app.new
216
- app.auth_provider.should_not_receive( :authenticate )
217
- app.auth_provider.should_not_receive( :authorize )
320
+ end
218
321
 
219
- app.handle( req )
220
- end
221
322
 
222
- it "passes a request for which the block returns false through auth" do
223
- req = @request_factory.get( '/api/v1/login' )
224
- req.notes[:skip_auth] = false
323
+ it "allows perms criteria to be declared with a string" do
324
+ @app.require_perms_for( '/string', :stringperm )
325
+ app = @app.new
225
326
 
226
- app = @app.new
227
- app.auth_provider.should_receive( :authenticate ).and_return( 'anonymous' )
228
- app.auth_provider.should_receive( :authorize ).and_return( true )
327
+ req = @request_factory.get( '/api/v1/string' )
328
+ app.required_perms_for( req ).should == [ :stringperm ]
329
+ req = @request_factory.get( '/api/v1/strong' )
330
+ app.required_perms_for( req ).should == []
331
+ end
229
332
 
230
- app.handle( req )
231
- end
333
+ it "allows perms criteria to be declared with a regexp" do
334
+ @app.require_perms_for( %r{^/admin}, :admin )
335
+ @app.require_perms_for( %r{/grant}, :grant )
336
+ app = @app.new
232
337
 
338
+ req = @request_factory.get( '/api/v1/admin' )
339
+ app.required_perms_for( req ).should == [ :admin ]
340
+ req = @request_factory.get( '/api/v1/admin/grant' )
341
+ app.required_perms_for( req ).should == [ :admin, :grant ]
342
+ req = @request_factory.get( '/api/v1/users' )
343
+ app.required_perms_for( req ).should == []
344
+ req = @request_factory.get( '/api/v1/users/grant' )
345
+ app.required_perms_for( req ).should == [ :grant ]
233
346
  end
234
347
 
348
+ it "allows perms criteria to be declared with a string and a block" do
349
+ @app.require_perms_for( '/string' ) do |req|
350
+ perms = [:stringperm, :otherperm]
351
+ perms << :rawdata if req.headers.accept && req.headers.accept =~ /json/i
352
+ perms
353
+ end
354
+ app = @app.new
235
355
 
236
- context "that has a negative auth criteria with both a pattern and a block" do
356
+ req = @request_factory.get( '/api/v1/string' )
357
+ app.required_perms_for( req ).should == [ :stringperm, :otherperm ]
358
+ req = @request_factory.get( '/api/v1/strong' )
359
+ app.required_perms_for( req ).should == []
360
+ end
237
361
 
238
- before( :each ) do
239
- @app.no_auth_for( %r{^/login/(?<username>\w+)} ) do |req, match|
240
- match[:username] != 'validuser'
241
- end
362
+ it "allows perms criteria to be declared with a regexp and a block" do
363
+ @app.require_perms_for( %r{^/admin(/(?<username>\w+))?} ) do |req, match|
364
+ perms = [:admin]
365
+ perms << match[:username].to_sym if match[:username]
366
+ perms
242
367
  end
368
+ app = @app.new
369
+
370
+ req = @request_factory.get( '/api/v1/admin' )
371
+ app.required_perms_for( req ).should == [ :admin ]
372
+ req = @request_factory.get( '/api/v1/admin/jzero' )
373
+ app.required_perms_for( req ).should == [ :admin, :jzero ]
374
+ req = @request_factory.get( '/api/v1/users' )
375
+ app.required_perms_for( req ).should == []
376
+ end
243
377
 
244
- it "knows that it has auth criteria" do
245
- @app.should have_auth_criteria()
378
+ it "allows perms criteria to be declared with just a block" do
379
+ @app.require_perms_for do |req|
380
+ req.app_path.scan( %r{/(\w+)} ).flatten.map( &:to_sym )
246
381
  end
382
+ app = @app.new
247
383
 
248
- it "doesn't pass a request through auth if the path matches and the block returns true" do
249
- req = @request_factory.get( '/api/v1/login/lyssa' )
384
+ req = @request_factory.get( '/api/v1/admin' )
385
+ app.required_perms_for( req ).should == [ :admin ]
386
+ req = @request_factory.get( '/api/v1/admin/jzero' )
387
+ app.required_perms_for( req ).should == [ :admin, :jzero ]
388
+ req = @request_factory.get( '/api/v1/users' )
389
+ app.required_perms_for( req ).should == [ :users ]
390
+ end
250
391
 
251
- app = @app.new
252
- app.auth_provider.should_not_receive( :authenticate )
253
- app.auth_provider.should_not_receive( :authorize )
392
+ it "allows negative perms criteria to be declared with a string" do
393
+ @app.no_perms_for( '/string' )
394
+ app = @app.new
254
395
 
255
- app.handle( req )
256
- end
396
+ req = @request_factory.get( '/api/v1/string' )
397
+ app.required_perms_for( req ).should be_empty()
398
+ req = @request_factory.get( '/api/v1/strong' )
399
+ app.required_perms_for( req ).should == [ :auth_test ] # default == appid
400
+ end
257
401
 
258
- it "passes a request through auth if the path doesn't match" do
259
- req = @request_factory.get( '/api/v1/console' )
402
+ it "allows negative perms criteria to be declared with a regexp" do
403
+ @app.no_perms_for( %r{^/signup} )
404
+ app = @app.new
260
405
 
261
- app = @app.new
262
- app.auth_provider.should_receive( :authenticate ).and_return( 'anonymous' )
263
- app.auth_provider.should_receive( :authorize ).and_return( true )
406
+ req = @request_factory.get( '/api/v1/signup' )
407
+ app.required_perms_for( req ).should be_empty()
408
+ req = @request_factory.get( '/api/v1/signup/reapply' )
409
+ app.required_perms_for( req ).should be_empty()
410
+ req = @request_factory.get( '/api/v1/index' )
411
+ app.required_perms_for( req ).should == [ :auth_test ]
412
+ end
264
413
 
265
- app.handle( req )
414
+ it "allows negative perms criteria to be declared with a string and a block" do
415
+ @app.no_perms_for( '/' ) do |req|
416
+ req.verb == :GET
266
417
  end
418
+ app = @app.new
267
419
 
268
- it "passes a request through auth if the path matches, but the the block returns false" do
269
- req = @request_factory.get( '/api/v1/login/validuser' )
420
+ req = @request_factory.get( '/api/v1' )
421
+ app.required_perms_for( req ).should be_empty()
422
+ req = @request_factory.post( '/api/v1' )
423
+ app.required_perms_for( req ).should == [ :auth_test ] # default == appid
424
+ req = @request_factory.get( '/api/v1/users' )
425
+ app.required_perms_for( req ).should == [ :auth_test ]
426
+ end
270
427
 
271
- app = @app.new
272
- app.auth_provider.should_receive( :authenticate ).and_return( 'anonymous' )
273
- app.auth_provider.should_receive( :authorize ).and_return( true )
428
+ it "allows negative perms criteria to be declared with a regexp and a block" do
429
+ @app.no_perms_for( %r{^/collection/(?<collname>[^/]+)} ) do |req, match|
430
+ public_collections = %w[degasse ione champhion]
431
+ collname = match[:collname]
432
+ if public_collections.include?( collname )
433
+ true
434
+ else
435
+ false
436
+ end
437
+ end
438
+ app = @app.new
274
439
 
275
- app.handle( req )
440
+ req = @request_factory.get( '/api/v1/collection' )
441
+ app.required_perms_for( req ).should == [ :auth_test ]
442
+ req = @request_factory.get( '/api/v1/collection/degasse' )
443
+ app.required_perms_for( req ).should be_empty()
444
+ req = @request_factory.get( '/api/v1/collection/ione' )
445
+ app.required_perms_for( req ).should be_empty()
446
+ req = @request_factory.get( '/api/v1/collection/champhion' )
447
+ app.required_perms_for( req ).should be_empty()
448
+ req = @request_factory.get( '/api/v1/collection/calindra' )
449
+ app.required_perms_for( req ).should == [ :auth_test ]
450
+ end
451
+
452
+ it "allows negative perms criteria to be declared with just a block" do
453
+ @app.no_perms_for do |req|
454
+ intranet = IPAddr.new( '10.0.0.0/8' )
455
+ intranet.include?( req.remote_ip )
276
456
  end
457
+ app = @app.new
277
458
 
459
+ req = @request_factory.get( '/api/v1/collection', x_forwarded_for: '10.0.1.68' )
460
+ app.required_perms_for( req ).should be_empty()
461
+ req = @request_factory.get( '/api/v1/collection', x_forwarded_for: '192.0.43.10' )
462
+ app.required_perms_for( req ).should == [ :auth_test ]
278
463
  end
279
464
 
280
465
 
281
466
  context "that has positive auth criteria" do
282
467
 
283
468
  before( :each ) do
284
- @app.require_auth_for( '/login' )
469
+ @app.require_auth_for( '/onlyauth' )
470
+ @app.require_auth_for( '/both' )
285
471
  end
286
472
 
287
- it "knows that it has auth criteria" do
288
- @app.should have_auth_criteria()
289
- end
473
+ context "and positive perms criteria" do
290
474
 
291
- it "passes requests that match through auth" do
292
- req = @request_factory.get( '/api/v1/login' )
475
+ before( :each ) do
476
+ @app.require_perms_for( '/both' )
477
+ @app.require_perms_for( '/onlyperms' )
478
+ end
293
479
 
294
- app = @app.new
295
- app.auth_provider.should_receive( :authenticate ).and_return( 'anonymous' )
296
- app.auth_provider.should_receive( :authorize ).and_return( true )
480
+ it "authorizes a request that only matches the perms criteria" do
481
+ req = @request_factory.get( '/api/v1/onlyperms' )
297
482
 
298
- app.handle( req )
299
- end
483
+ app = @app.new
484
+ app.auth_provider.should_not_receive( :authenticate )
485
+ app.auth_provider.should_receive( :authorize )
300
486
 
301
- it "doesn't pass requests that don't match through auth" do
302
- req = @request_factory.get( '/api/v1/console' )
487
+ app.handle( req )
488
+ end
303
489
 
304
- app = @app.new
305
- app.auth_provider.should_not_receive( :authenticate )
306
- app.auth_provider.should_not_receive( :authorize )
490
+ it "authenticates a request that only matches the auth criteria" do
491
+ req = @request_factory.get( '/api/v1/onlyauth' )
307
492
 
308
- app.handle( req )
309
- end
310
- end
493
+ app = @app.new
494
+ app.auth_provider.should_receive( :authenticate )
495
+ app.auth_provider.should_not_receive( :authorize )
311
496
 
497
+ app.handle( req )
498
+ end
312
499
 
313
- context "that has a positive auth criteria block" do
500
+ it "authenticates and authorizes a request that matches both" do
501
+ req = @request_factory.get( '/api/v1/both' )
314
502
 
315
- before( :each ) do
316
- @app.require_auth_for do |req|
317
- req.notes[:require_auth]
503
+ app = @app.new
504
+ app.auth_provider.should_receive( :authenticate )
505
+ app.auth_provider.should_receive( :authorize )
506
+
507
+ app.handle( req )
318
508
  end
319
- end
320
509
 
321
- it "knows that it has auth criteria" do
322
- @app.should have_auth_criteria()
323
- end
510
+ it "doesn't do either for a request that doesn't match either" do
511
+ req = @request_factory.get( '/api/v1/neither' )
324
512
 
325
- it "passes requests for which the block returns true through auth" do
326
- req = @request_factory.get( '/api/v1/login' )
327
- req.notes[:require_auth] = true
513
+ app = @app.new
514
+ app.auth_provider.should_not_receive( :authenticate )
515
+ app.auth_provider.should_not_receive( :authorize )
328
516
 
329
- app = @app.new
330
- app.auth_provider.should_receive( :authenticate ).and_return( 'anonymous' )
331
- app.auth_provider.should_receive( :authorize ).and_return( true )
517
+ app.handle( req )
518
+ end
332
519
 
333
- app.handle( req )
334
520
  end
335
521
 
336
- it "doesn't pass requests for which the block returns false through auth" do
337
- req = @request_factory.get( '/api/v1/console' )
338
- req.notes[:require_auth] = false
522
+ context "and negative perms criteria" do
339
523
 
340
- app = @app.new
341
- app.auth_provider.should_not_receive( :authenticate )
342
- app.auth_provider.should_not_receive( :authorize )
524
+ before( :each ) do
525
+ @app.no_perms_for( '/both' )
526
+ @app.no_perms_for( '/onlyperms' )
527
+ end
528
+
529
+ it "doesn't do either for a request that only matches the perms criteria" do
530
+ req = @request_factory.get( '/api/v1/onlyperms' )
531
+
532
+ app = @app.new
533
+ app.auth_provider.should_not_receive( :authenticate )
534
+ app.auth_provider.should_not_receive( :authorize )
535
+
536
+ app.handle( req )
537
+ end
538
+
539
+ it "authenticates and authorizes a request that only matches the auth criteria"do
540
+ req = @request_factory.get( '/api/v1/onlyauth' )
541
+
542
+ app = @app.new
543
+ app.auth_provider.should_receive( :authenticate )
544
+ app.auth_provider.should_receive( :authorize )
545
+
546
+ app.handle( req )
547
+ end
548
+
549
+ it "only authenticates a request that matches both" do
550
+ req = @request_factory.get( '/api/v1/both' )
551
+
552
+ app = @app.new
553
+ app.auth_provider.should_receive( :authenticate )
554
+ app.auth_provider.should_not_receive( :authorize )
555
+
556
+ app.handle( req )
557
+ end
558
+
559
+ it "only authorizes a request that doesn't match either" do
560
+ req = @request_factory.get( '/api/v1/neither' )
561
+
562
+ app = @app.new
563
+ app.auth_provider.should_not_receive( :authenticate )
564
+ app.auth_provider.should_receive( :authorize )
565
+
566
+ app.handle( req )
567
+ end
343
568
 
344
- app.handle( req )
345
569
  end
570
+
346
571
  end
347
572
 
348
573
 
349
- context "that has a positive auth criteria with both a pattern and a block" do
574
+ context "that has negative auth criteria" do
350
575
 
351
576
  before( :each ) do
352
- @app.require_auth_for( %r{^/login/(?<username>\w+)} ) do |req, match|
353
- match[:username] != 'guest'
354
- end
577
+ @app.no_auth_for( '/onlyauth' )
578
+ @app.no_auth_for( '/both' )
355
579
  end
356
580
 
357
- it "knows that it has auth criteria" do
358
- @app.should have_auth_criteria()
359
- end
581
+ context "and positive perms criteria" do
360
582
 
361
- it "passes a request through auth if the path matches and the block returns true" do
362
- req = @request_factory.get( '/api/v1/login/lyssa' )
583
+ before( :each ) do
584
+ @app.require_perms_for( '/both' )
585
+ @app.require_perms_for( '/onlyperms' )
586
+ end
363
587
 
364
- app = @app.new
365
- app.auth_provider.should_receive( :authenticate ).and_return( 'lyssa' )
366
- app.auth_provider.should_receive( :authorize ).and_return( true )
588
+ it "authenticates and authorizes a request that only matches the perms criteria" do
589
+ req = @request_factory.get( '/api/v1/onlyperms' )
367
590
 
368
- app.handle( req )
369
- end
591
+ app = @app.new
592
+ app.auth_provider.should_receive( :authenticate )
593
+ app.auth_provider.should_receive( :authorize )
370
594
 
371
- it "doesn't pass a request through auth if the path doesn't match" do
372
- req = @request_factory.get( '/api/v1/console' )
595
+ app.handle( req )
596
+ end
373
597
 
374
- app = @app.new
375
- app.auth_provider.should_not_receive( :authenticate )
376
- app.auth_provider.should_not_receive( :authorize )
598
+ it "doesn't do either for a request that only matches the auth criteria" do
599
+ req = @request_factory.get( '/api/v1/onlyauth' )
377
600
 
378
- app.handle( req )
379
- end
601
+ app = @app.new
602
+ app.auth_provider.should_not_receive( :authenticate )
603
+ app.auth_provider.should_not_receive( :authorize )
380
604
 
381
- it "doesn't pass a request through auth if the path matches, but the the block returns false" do
382
- req = @request_factory.get( '/api/v1/login/guest' )
605
+ app.handle( req )
606
+ end
383
607
 
384
- app = @app.new
385
- app.auth_provider.should_not_receive( :authenticate )
386
- app.auth_provider.should_not_receive( :authorize )
608
+ it "authorizes a request that matches both" do
609
+ req = @request_factory.get( '/api/v1/both' )
610
+
611
+ app = @app.new
612
+ app.auth_provider.should_not_receive( :authenticate )
613
+ app.auth_provider.should_receive( :authorize )
614
+
615
+ app.handle( req )
616
+ end
617
+
618
+ it "authenticates a request that doesn't match either" do
619
+ req = @request_factory.get( '/api/v1/neither' )
620
+
621
+ app = @app.new
622
+ app.auth_provider.should_receive( :authenticate )
623
+ app.auth_provider.should_not_receive( :authorize )
624
+
625
+ app.handle( req )
626
+ end
387
627
 
388
- app.handle( req )
389
628
  end
390
629
 
391
- end
630
+ context "and negative perms criteria" do
392
631
 
632
+ before( :each ) do
633
+ @app.no_perms_for( '/both' )
634
+ @app.no_perms_for( '/onlyperms' )
635
+ end
393
636
 
394
- it "can register an authorization callback with a block" do
395
- @app.authz_callback { :authz }
396
- @app.authz_callback.should be_a( Proc )
397
- end
637
+ it "authenticates for a request that only matches the perms criteria" do
638
+ req = @request_factory.get( '/api/v1/onlyperms' )
639
+
640
+ app = @app.new
641
+ app.auth_provider.should_receive( :authenticate )
642
+ app.auth_provider.should_not_receive( :authorize )
643
+
644
+ app.handle( req )
645
+ end
646
+
647
+ it "authorizes a request that only matches the auth criteria" do
648
+ req = @request_factory.get( '/api/v1/onlyauth' )
649
+
650
+ app = @app.new
651
+ app.auth_provider.should_not_receive( :authenticate )
652
+ app.auth_provider.should_receive( :authorize )
653
+
654
+ app.handle( req )
655
+ end
656
+
657
+ it "doesn't do either for a request that matches both" do
658
+ req = @request_factory.get( '/api/v1/both' )
659
+
660
+ app = @app.new
661
+ app.auth_provider.should_not_receive( :authenticate )
662
+ app.auth_provider.should_not_receive( :authorize )
663
+
664
+ app.handle( req )
665
+ end
666
+
667
+ it "authenticates and authorizes a request that doesn't match either" do
668
+ req = @request_factory.get( '/api/v1/neither' )
669
+
670
+ app = @app.new
671
+ app.auth_provider.should_receive( :authenticate )
672
+ app.auth_provider.should_receive( :authorize )
673
+
674
+ app.handle( req )
675
+ end
676
+
677
+ end
398
678
 
399
- it "can register an authorization callback with a callable object" do
400
- callback = Proc.new { :authz }
401
- @app.authz_callback( callback )
402
- @app.authz_callback.should == callback
403
679
  end
404
680
 
405
681
 
406
- context "that has an authz callback" do
682
+ context "that has overlapping perms criteria" do
407
683
 
408
684
  before( :each ) do
409
- @app.authz_callback { }
685
+ @app.require_perms_for( %r{^/admin.*}, :admin )
686
+ @app.require_perms_for( %r{^/admin/upload.*}, :upload )
410
687
  end
411
688
 
412
- it "yields authorization to the callback if authentication succeeds"
413
- it "responds with a 403 Forbidden response if the block doesn't return true"
689
+ it "authorizes with a union of the permissions of both of the criteria" do
690
+ req = @request_factory.get( '/api/v1/admin/upload' )
691
+
692
+ app = @app.new
693
+ app.auth_provider.stub!( :authenticate ).and_return( :credentials )
694
+ app.auth_provider.should_receive( :authorize ).with( :credentials, req, [:admin, :upload] )
695
+
696
+ app.handle( req )
697
+ end
414
698
 
415
699
  end
416
700