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

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -11,7 +11,6 @@ require 'strelka/plugins'
11
11
  require 'strelka/httprequest/auth'
12
12
  require 'strelka/authprovider'
13
13
 
14
-
15
14
  # Pluggable authentication and authorization for Strelka applications.
16
15
  #
17
16
  # Enabling the +:auth+ plugin by default causes all requests to your
@@ -31,6 +30,13 @@ require 'strelka/authprovider'
31
30
  # can implement one or both of the stages; see the API docs for Strelka::AuthProvider
32
31
  # for details on how to write your own plugin.
33
32
  #
33
+ # The provider for an application can be specified in the Configurability config file
34
+ # under the 'auth' section:
35
+ #
36
+ # ---
37
+ # auth:
38
+ # provider: basic
39
+ #
34
40
  #
35
41
  # == Applying Authentication
36
42
  #
@@ -123,20 +129,41 @@ require 'strelka/authprovider'
123
129
  # itself (especially a custom one), but typically authorization is particular to an application and
124
130
  # even particular actions within the application.
125
131
  #
126
- # To provide the particulars for your app's authorization, you can declare
127
- # a block with the +authz_callback+ directive.
132
+ # To facilitate mapping out what actions are available to whom, there is a
133
+ # declaration similar to require_auth_for that can define a set of permissions
134
+ # that are necessary for a request to be allowed:
128
135
  #
129
- # authz_callback do |request, user, *other_auth_info|
130
- # user.can?( request.app_path )
131
- # end
136
+ # # The app ID, which is the default permission
137
+ # ID = 'gemserver'
138
+ #
139
+ # # GET /app/admin/upload/install would require:
140
+ # # :gemserver, :admin, :upload, and :install
141
+ # # permissions. What those mean is up to the AuthProvider.
142
+ # require_perms_for ''
143
+ # require_perms_for %r{^/admin.*}, :admin
144
+ # require_perms_for %r{/upload}, :upload
145
+ # require_perms_for %r{/install}, :install
146
+ #
147
+ # and its negative corollary:
148
+ #
149
+ # no_perms_for '/login'
150
+ #
151
+ # Incoming requests are matched against +require_perms_for+ patterns, and the union
152
+ # of all matching permissions is gathered, then any +no_auth_for+ patterns
153
+ # are used to remove permissions from that set.
154
+ #
155
+ # If no require_perms_for patterns are declared, authorization is not checked, unless there is
156
+ # at least one no_perms_for pattern, in which case all requests that don't match the negative
157
+ # patterns are checked (with the permission set to the ID of the app).
158
+ #
159
+ # Authorization will be checked once authentication has succeeded. It will be called with at least the
160
+ # credentials object returned from the authentication stage and the request object. Some AuthProviders
161
+ # may opt to return authentication credentials as a User object of some kind (e.g., a database row,
162
+ # LDAP entry, model object, etc.), but the simpler ones just return the login of the authenticated
163
+ # +user+. The AuthProvider may also furnish additional useful arguments such as a database handle,
164
+ # permission objects, etc. to your authorization block. See the documentation for your chosen
165
+ # AuthProvider for details.
132
166
  #
133
- # The block will be called once authentication has succeeded, and any general authorization has been
134
- # checked. It will be called with at least the credentials object returned from the authentication
135
- # stage and the request object. Some AuthProviders may opt to return authentication credentials
136
- # as a User object of some kind (e.g., a database row, LDAP entry, model object, etc.), but the
137
- # simpler ones just return the login of the authenticated +user+. The AuthProvider may also
138
- # furnish additional useful arguments such as a database handle, permission objects, etc. to your
139
- # authorization block. See the documentation for your chosen AuthProvider for details.
140
167
  #
141
168
  # == Customizing Failure
142
169
  #
@@ -151,11 +178,10 @@ require 'strelka/authprovider'
151
178
  #
152
179
  # If you're using form-based session authentication (as opposed to basic
153
180
  # auth, which has its own UI), you can rewrite the response to instruct
154
- # the browser to go to a static HTML form instead:
181
+ # the browser to go to a static HTML form instead using the <tt>:errors</tt> plugin:
155
182
  #
156
183
  # class FormAuthApp < Strelka::App
157
184
  # plugins :errors, :auth, :sessions
158
- # auth_provider :session
159
185
  #
160
186
  # on_status HTTP::AUTH_REQUIRED do |res, status|
161
187
  # formuri = res.request.uri
@@ -178,7 +204,6 @@ require 'strelka/authprovider'
178
204
  #
179
205
  # class TemplateFormAuthApp < Strelka::App
180
206
  # plugins :auth, :errors, :templating
181
- # auth_provider :session
182
207
  #
183
208
  # layout 'examples/layout.tmpl'
184
209
  # templates \
@@ -194,58 +219,18 @@ require 'strelka/authprovider'
194
219
  #
195
220
  # end
196
221
  #
197
- # == Examples
198
- #
199
- # Here are a few more examples using a few different AuthProviders.
200
- #
201
- # # Guard every request the app does behind a simple passphrase
202
- # class MyGuardedApp < Strelka::App
203
- # plugins :auth
204
- #
205
- # auth_provider :passphrase
206
- # end
207
- #
208
- # # Require LDAP authentication for one route
209
- # class MyGuardedApp < Strelka::App
210
- # plugins :auth, :routing
211
- #
212
- # auth_provider :ldap
213
- # authz_callback do |user, request, directory|
214
- # authgroup = directory.ou( :appperms ).cn( :guarded_app )
215
- # authgroup.members.include?( user.dn )
216
- # end
217
- #
218
- # authenticated %r{^/admin}
219
- # end
220
- #
221
- # # Use a user table in a PostgreSQL database for authentication for
222
- # # all routes except one
223
- # class MyGuardedApp < Strelka::App
224
- # plugins :auth
225
- #
226
- # auth_provider :sequel
227
- # authz_callback do |user, request, db|
228
- # db[:permissions].filter( :user_id => user[:id] ).
229
- # filter( :permname => 'guardedapp' )
230
- # end
231
- #
232
- # unauthenticated %r{^/auth}
233
- #
234
- # # Only authenticated users can use this
235
- # post '/servers' do
236
- # # ...
237
- # end
238
- # end
239
- #
240
222
  module Strelka::App::Auth
241
223
  extend Strelka::Plugin,
242
224
  Strelka::MethodUtilities,
243
- Loggability
225
+ Loggability,
226
+ Configurability
244
227
  include Strelka::Constants
245
228
 
246
229
  # Loggability API -- set up logging under the 'strelka' log host
247
230
  log_to :strelka
248
231
 
232
+ # Configurability API -- configure the auth plugin via the 'auth' section of the config
233
+ config_key :auth
249
234
 
250
235
  # Plugins API -- Set up load order
251
236
  run_before :routing, :restresources
@@ -255,60 +240,89 @@ module Strelka::App::Auth
255
240
  # The name of the default plugin to use for authentication
256
241
  DEFAULT_AUTH_PROVIDER = :hostaccess
257
242
 
243
+ # Configuration defaults
244
+ CONFIG_DEFAULTS = {
245
+ provider: DEFAULT_AUTH_PROVIDER,
246
+ }
247
+
248
+
249
+
250
+ ##
251
+ # The Array of apps that have had the auth plugin installed; this is used to
252
+ # set up the AuthProvider when the configuration loads later.
253
+ singleton_attr_accessor :extended_apps
254
+ self.extended_apps = []
255
+
256
+
257
+ ### Configurability API -- configure the Auth plugin via the 'auth' section of the
258
+ ### unified config.
259
+ def self::configure( config=nil )
260
+ if config && config.provider?
261
+ self.log.debug "Setting up the %p AuthProvider for apps: %p" %
262
+ [ config.provider, self.extended_apps ]
263
+ self.extended_apps.each {|app| app.auth_provider = config.provider }
264
+ else
265
+ self.log.notice "Setting up the default AuthProvider for apps %p" % [ self.extended_apps ]
266
+ self.extended_apps.each {|app| app.auth_provider = DEFAULT_AUTH_PROVIDER }
267
+ end
268
+ end
269
+
258
270
 
259
271
  # Class methods to add to app classes that enable Auth
260
272
  module ClassMethods
261
273
 
262
- @auth_provider = nil
263
- @authz_callback = nil
264
- @positive_auth_criteria = {}
265
- @negative_auth_criteria = {}
274
+ ### Extension callback -- register objects that are extended so when the
275
+ ### auth plugin is configured, it can set the configured auto provider.
276
+ def self::extended( obj )
277
+ super
278
+ Strelka::App::Auth.extended_apps << obj
279
+ obj.auth_provider = Strelka::App::Auth::DEFAULT_AUTH_PROVIDER
280
+ end
281
+
282
+
283
+ @auth_provider = nil
284
+
285
+ @positive_auth_criteria = {}
286
+ @negative_auth_criteria = {}
287
+
288
+ @positive_perms_criteria = {}
289
+ @negative_perms_criteria = {}
290
+
291
+
292
+ ##
293
+ # The Strelka::AuthProvider subclass that will be used to provide authentication and
294
+ # authorization to instances of the app.
295
+ attr_reader :auth_provider
266
296
 
267
297
  ##
268
- # Arrays of criteria for applying and skipping auth for a request.
298
+ # Hashes of criteria for applying and skipping auth for a request, keyed by request pattern
269
299
  attr_reader :positive_auth_criteria, :negative_auth_criteria
270
300
 
301
+ ##
302
+ # Hashes of criteria for applying and skipping authorization for a request, keyed by request pattern
303
+ attr_reader :positive_perms_criteria, :negative_perms_criteria
304
+
271
305
 
272
306
  ### Extension callback -- add instance variables to extending objects.
273
307
  def inherited( subclass )
274
308
  super
275
309
  subclass.instance_variable_set( :@auth_provider, @auth_provider )
276
- subclass.instance_variable_set( :@authz_callback, @authz_callback.dup ) if @authz_callback
277
310
  subclass.instance_variable_set( :@positive_auth_criteria, @positive_auth_criteria.dup )
278
311
  subclass.instance_variable_set( :@negative_auth_criteria, @negative_auth_criteria.dup )
312
+ subclass.instance_variable_set( :@positive_perms_criteria, @positive_perms_criteria.dup )
313
+ subclass.instance_variable_set( :@negative_perms_criteria, @negative_perms_criteria.dup )
279
314
  end
280
315
 
281
316
 
282
- ### Get/set the authentication type.
283
- def auth_provider( type=nil )
284
- if type
285
- @auth_provider = Strelka::AuthProvider.get_subclass( type )
286
- end
287
-
288
- self.log.debug "Auth provider %p" % [ @auth_provider ]
289
- @auth_provider ||= Strelka::AuthProvider.get_subclass( DEFAULT_AUTH_PROVIDER )
290
- self.log.debug "Auth provider %p" % [ @auth_provider ]
317
+ ### Get/set the AuthProvider for the app to +type+, where +type+ can be an AuthProvider
318
+ ### class object or the name of one.
319
+ def auth_provider=( type )
320
+ @auth_provider = Strelka::AuthProvider.get_subclass( type )
321
+ self.log.debug "Auth provider set to %p" % [ @auth_provider ]
291
322
  return @auth_provider
292
323
  end
293
324
 
294
325
 
295
- ### Register a function to call after the user successfully authenticates to check
296
- ### for authorization or other criteria. The arguments to the function depend on
297
- ### which authentication plugin is used. Returning +true+ from this function will
298
- ### cause authorization to succeed, while returning a false value causes it to fail
299
- ### with a FORBIDDEN response. If no callback is set, and the provider doesn't
300
- ### provide authorization
301
- def authz_callback( callable=nil, &block )
302
- if callable
303
- @authz_callback = callable
304
- elsif block
305
- @authz_callback = block
306
- end
307
-
308
- return @authz_callback
309
- end
310
-
311
-
312
326
  ### Returns +true+ if there are any criteria for determining whether or
313
327
  ### not a request needs auth.
314
328
  def has_auth_criteria?
@@ -329,13 +343,19 @@ module Strelka::App::Auth
329
343
  return !self.negative_auth_criteria.empty?
330
344
  end
331
345
 
332
-
333
- ### Constrain auth to apply only to requests which match the given +criteria+,
334
- ### and/or the given +block+. The +criteria+ are either Strings or Regexps
335
- ### which are tested against
336
- ### {the request's #app_path}[rdoc-ref:Strelka::HTTPRequest#app_path]. The block
337
- ### should return a true-ish value if the request should undergo authentication
338
- ### and authorization.
346
+ ### :call-seq:
347
+ ### require_auth_for( string )
348
+ ### require_auth_for( regexp )
349
+ ### require_auth_for { |request| ... }
350
+ ### require_auth_for( string ) { |request| ... }
351
+ ### require_auth_for( regexp ) { |request, matchdata| ... }
352
+ ###
353
+ ### Constrain authentication to apply only to requests whose
354
+ ### {#app_path}[rdoc-ref:Strelka::HTTPRequest#app_path] matches
355
+ ### the given +string+ or +regexp+, and/or for which the given +block+ returns
356
+ ### a true value. +regexp+ patterns are matched as-is, and +string+ patterns are
357
+ ### matched exactly via <tt>==</tt> after stripping leading and trailing '/' characters
358
+ ### from both it and the #app_path.
339
359
  ### *NOTE:* using this declaration inverts the default security policy of
340
360
  ### restricting access to all requests.
341
361
  def require_auth_for( *criteria, &block )
@@ -355,8 +375,17 @@ module Strelka::App::Auth
355
375
  end
356
376
 
357
377
 
358
- ### Contrain auth to apply to all requests *except* those that match the
359
- ### given +criteria+.
378
+ ### :call-seq:
379
+ ### no_auth_for( string )
380
+ ### no_auth_for( regexp )
381
+ ### no_auth_for { |request| ... }
382
+ ### no_auth_for( string ) { |request| ... }
383
+ ### no_auth_for( regexp ) { |request, matchdata| ... }
384
+ ###
385
+ ### Constrain authentication to apply to requests *except* those whose
386
+ ### {#app_path}[rdoc-ref:Strelka::HTTPRequest#app_path] matches
387
+ ### the given +string+ or +regexp+, and/or for which the given +block+ returns
388
+ ### a true value.
360
389
  def no_auth_for( *criteria, &block )
361
390
  if self.has_positive_auth_criteria?
362
391
  raise ScriptError,
@@ -374,6 +403,39 @@ module Strelka::App::Auth
374
403
  end
375
404
 
376
405
 
406
+ ### Constrain authorization to apply only to requests which match the given
407
+ ### +pattern+. The +pattern+ is either a String or a Regexp which is tested against
408
+ ### {the request's #app_path}[rdoc-ref:Strelka::HTTPRequest#app_path]. The +perms+ should
409
+ ### be Symbols which indicate a set of permission types that must have been granted
410
+ ### in order to carry out the request. The block should also return one or more
411
+ ### permissions (as Symbols) if the request should undergo authorization, or nil
412
+ ### if it should not.
413
+ ### *NOTE:* using this declaration inverts the default security policy of
414
+ ### restricting access to all requests.
415
+ def require_perms_for( pattern=nil, *perms, &block )
416
+ block ||= Proc.new { perms }
417
+
418
+ pattern.gsub!( %r{^/+|/+$}, '' ) if pattern.respond_to?( :gsub! )
419
+ self.log.debug " adding require_perms (%p) for %p" % [ perms, pattern ]
420
+ self.positive_perms_criteria[ pattern ] = block
421
+ end
422
+
423
+
424
+ ### Register one or more exceptions to the permissions policy in effect for
425
+ ### requests whose {#app_path}[rdoc-ref:Strelka::HTTPRequest#app_path]
426
+ ### matches the specified +pattern+. The +block+ form should return +true+ if the request
427
+ ### it's called with should be allowed without authorization checks.
428
+ def no_perms_for( pattern=nil, &block )
429
+ raise LocalJumpError, "no block or pattern given" unless pattern || block
430
+
431
+ block ||= Proc.new { true }
432
+ pattern ||= /(?##{block.object_id})/
433
+
434
+ pattern.gsub!( %r{^/+|/+$}, '' ) if pattern.respond_to?( :gsub! )
435
+ self.log.debug " adding no_auth for %p" % [ pattern ]
436
+ self.negative_perms_criteria[ pattern ] = block
437
+ end
438
+
377
439
  end # module ClassMethods
378
440
 
379
441
 
@@ -407,15 +469,42 @@ module Strelka::App::Auth
407
469
  def handle_request( request, &block )
408
470
  self.log.debug "[:auth] Wrapping request in auth with a %p" % [ self.auth_provider ]
409
471
 
410
- self.authenticate_and_authorize( request ) if self.request_should_auth?( request )
472
+ self.authenticate_and_authorize( request )
411
473
 
412
474
  super
413
475
  end
414
476
 
415
477
 
416
- #########
417
- protected
418
- #########
478
+ ### Process authentication and authorization for the specified +request+.
479
+ def authenticate_and_authorize( request )
480
+ credentials = nil
481
+ credentials = self.provide_authentication( request ) if self.request_should_auth?( request )
482
+ request.authenticated_user = credentials
483
+
484
+ self.provide_authorization( credentials, request )
485
+ end
486
+
487
+
488
+ ### If the AuthProvider does authentication, try to extract authenticated credentials
489
+ ### from the +request+ and return them, throwing a :finish with
490
+ ### a properly-constructed 401 (Auth required) response if that fails.
491
+ def provide_authentication( request )
492
+ provider = self.auth_provider
493
+ self.log.info "Authenticating request using provider: %p" % [ provider ]
494
+ return provider.authenticate( request )
495
+ end
496
+
497
+
498
+ ### Process authorization for the given +credentials+ and +request+.
499
+ ### The +credentials+ argument is the opaque return value from a valid authentication, or
500
+ ### +nil+ if the request didn't require authentication.
501
+ def provide_authorization( credentials, request )
502
+ provider = self.auth_provider
503
+ perms = self.required_perms_for( request )
504
+ self.log.debug "Perms required: %p" % [ perms ]
505
+ provider.authorize( credentials, request, perms ) unless perms.empty?
506
+ end
507
+
419
508
 
420
509
  ### Returns +true+ if the given +request+ requires authentication.
421
510
  def request_should_auth?( request )
@@ -428,19 +517,21 @@ module Strelka::App::Auth
428
517
  criteria = self.class.positive_auth_criteria
429
518
  self.log.debug " checking %d positive auth criteria" % [ criteria.length ]
430
519
  return criteria.any? do |pattern, block|
431
- self.log.debug " %p -> %p" % [ pattern, block ]
432
520
  self.request_matches_criteria( request, pattern, &block )
433
521
  end
522
+ return false
434
523
 
435
524
  # If there are negative criteria, return false if the request matches any of them,
436
525
  # or true if they don't
437
526
  elsif self.class.has_negative_auth_criteria?
438
527
  criteria = self.class.negative_auth_criteria
439
528
  self.log.debug " checking %d negative auth criteria" % [ criteria.length ]
440
- return !criteria.any? do |pattern, block|
441
- self.log.debug " %p -> %p" % [ pattern, block ]
442
- self.request_matches_criteria( request, pattern, &block )
529
+ return false if criteria.any? do |pattern, block|
530
+ rval = self.request_matches_criteria( request, pattern, &block )
531
+ self.log.debug " matched: %p -> %p" % [ pattern, block ] if rval
532
+ rval
443
533
  end
534
+ return true
444
535
 
445
536
  else
446
537
  self.log.debug " no auth criteria; default to requiring auth"
@@ -449,24 +540,56 @@ module Strelka::App::Auth
449
540
  end
450
541
 
451
542
 
543
+ ### Return a permission Symbol derived from the app's ID.
544
+ def default_permission
545
+ return self.app_id.downcase.gsub(/\W+/, '_' ).to_sym
546
+ end
547
+
548
+
549
+ ### Gather the set of permissions that apply to the specified +request+ and return
550
+ ### them.
551
+ def required_perms_for( request )
552
+ self.log.debug "Gathering required perms for: %s %s" % [ request.verb, request.app_path ]
553
+
554
+ # Return the empty set if any negative auth criteria match
555
+ return [] if self.negative_perms_criteria_match?( request )
556
+
557
+ # If there aren't any positive criteria, default to requiring authorization with
558
+ # the app's ID as the permission
559
+ if self.class.positive_perms_criteria.empty?
560
+ return [ self.default_permission ]
561
+ end
562
+
563
+ # Apply positive auth criteria
564
+ return self.union_positive_perms_criteria( request )
565
+ end
566
+
567
+
568
+ #########
569
+ protected
570
+ #########
571
+
452
572
  ### Returns +true+ if there are positive auth criteria and the +request+ matches
453
573
  ### at least one of them.
454
574
  def request_matches_criteria( request, pattern )
575
+ self.log.debug "Testing request '%s %s' against pattern: %p" %
576
+ [ request.verb, request.app_path, pattern ]
577
+
455
578
  case pattern
456
579
  when nil
457
580
  self.log.debug " no pattern; calling the block"
458
581
  return yield( request )
459
582
 
460
583
  when Regexp
461
- self.log.debug " matching app_path with regexp: %p" % [ pattern ]
584
+ self.log.debug " checking app_path with regexp: %p" % [ pattern ]
462
585
  matchdata = pattern.match( request.app_path ) or return false
463
- self.log.debug " calling the block"
586
+ self.log.debug " matched: calling the block"
464
587
  return yield( request, matchdata )
465
588
 
466
589
  when String
467
- self.log.debug " matching app_path: %p" % [ pattern ]
590
+ self.log.debug " checking app_path: %p" % [ pattern ]
468
591
  request.app_path.gsub( %r{^/+|/+$}, '' ) == pattern or return false
469
- self.log.debug " calling the block"
592
+ self.log.debug " matched: calling the block"
470
593
  return yield( request )
471
594
 
472
595
  else
@@ -475,31 +598,36 @@ module Strelka::App::Auth
475
598
  end
476
599
 
477
600
 
478
- ### Process authentication and authorization for the specified +request+.
479
- def authenticate_and_authorize( request )
480
- credentials = self.provide_authentication( request )
481
- request.authenticated_user = credentials
482
- self.provide_authorization( credentials, request )
601
+ ### Returns +true+ if the +request+ matches at least one negative perms criteria
602
+ ### whose block also returns +true+ when called.
603
+ def negative_perms_criteria_match?( request )
604
+ self.log.debug " negative perm criteria: %p" % [ self.class.negative_perms_criteria ]
605
+ return self.class.negative_perms_criteria.any? do |pattern, block|
606
+ self.request_matches_criteria( request, pattern, &block )
607
+ end
483
608
  end
484
609
 
485
610
 
486
- ### If the AuthProvider does authentication, try to extract authenticated credentials
487
- ### from the +request+ and return them, throwing a :finish with
488
- ### a properly-constructed 401 (Auth required) response if that fails.
489
- def provide_authentication( request )
490
- provider = self.auth_provider
491
- self.log.info "Authenticating request using provider: %p" % [ provider ]
492
- return provider.authenticate( request )
493
- end
611
+ ### Find all positive perm criteria, calling each one's block with +request+ if its
612
+ ### pattern matches +path+, and assembling a union of all the permission sets
613
+ ### that result.
614
+ def union_positive_perms_criteria( request )
615
+ perms = []
494
616
 
617
+ self.log.debug " positive perm criteria: %p" % [ self.class.positive_perms_criteria ]
618
+ self.class.positive_perms_criteria.each do |pattern, block|
619
+ newperms = self.request_matches_criteria( request, pattern, &block ) or next
620
+ newperms = Array( newperms )
621
+ newperms << self.default_permission if newperms.empty?
495
622
 
496
- ### Process authorization for the given +credentials+ and +request+.
497
- def provide_authorization( credentials, request )
498
- provider = self.auth_provider
499
- callback = self.class.authz_callback
623
+ raise TypeError, "Permissions must be Symbols; got: %p" % [newperms] unless
624
+ newperms.all? {|perm| perm.is_a?(Symbol) }
625
+
626
+ self.log.debug " found new perms: %p" % [ newperms ]
627
+ perms += newperms
628
+ end
500
629
 
501
- self.log.info "Authorizing using credentials: %p, callback: %p" % [ credentials, callback ]
502
- provider.authorize( credentials, request, &callback )
630
+ return perms.compact.uniq
503
631
  end
504
632
 
505
633