Capcode 0.8.4 → 0.8.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. data/README.rdoc +58 -0
  2. data/doc/rdoc/classes/Capcode.html +938 -0
  3. data/doc/rdoc/classes/Capcode/Base.html +136 -0
  4. data/doc/rdoc/classes/Capcode/HTTPError.html +134 -0
  5. data/doc/rdoc/classes/Capcode/Helpers.html +608 -0
  6. data/doc/rdoc/classes/Capcode/Helpers/Authorization.html +188 -0
  7. data/doc/rdoc/classes/Capcode/Mab.html +118 -0
  8. data/doc/rdoc/classes/Capcode/Resource.html +111 -0
  9. data/doc/rdoc/classes/Capcode/Views.html +112 -0
  10. data/doc/rdoc/created.rid +1 -0
  11. data/doc/rdoc/files/AUTHORS.html +107 -0
  12. data/doc/rdoc/files/COPYING.html +531 -0
  13. data/doc/rdoc/files/README_rdoc.html +601 -0
  14. data/doc/rdoc/files/lib/capcode/base/db_rb.html +101 -0
  15. data/doc/rdoc/files/lib/capcode/helpers/auth_rb.html +132 -0
  16. data/doc/rdoc/files/lib/capcode/render/erb_rb.html +108 -0
  17. data/doc/rdoc/files/lib/capcode/render/haml_rb.html +108 -0
  18. data/doc/rdoc/files/lib/capcode/render/json_rb.html +108 -0
  19. data/doc/rdoc/files/lib/capcode/render/markaby_rb.html +108 -0
  20. data/doc/rdoc/files/lib/capcode/render/sass_rb.html +108 -0
  21. data/doc/rdoc/files/lib/capcode/render/static_rb.html +101 -0
  22. data/doc/rdoc/files/lib/capcode/render/text_rb.html +101 -0
  23. data/doc/rdoc/files/lib/capcode/render/webdav_rb.html +124 -0
  24. data/doc/rdoc/files/lib/capcode/render/xml_rb.html +101 -0
  25. data/doc/rdoc/files/lib/capcode_rb.html +119 -0
  26. data/doc/rdoc/fr_class_index.html +34 -0
  27. data/doc/rdoc/fr_file_index.html +41 -0
  28. data/doc/rdoc/fr_method_index.html +44 -0
  29. data/doc/rdoc/index.html +24 -0
  30. data/doc/rdoc/rdoc-style.css +208 -0
  31. data/examples/auth-basic.rb +46 -0
  32. data/examples/auth-digest.rb +47 -0
  33. data/examples/auth-webdav.rb +29 -0
  34. data/examples/blog-couchdb-run.rb +10 -0
  35. data/examples/blog-couchdb.rb +8 -8
  36. data/examples/blog-couchdb.ru +12 -0
  37. data/examples/render-static.rb +1 -1
  38. data/examples/render-static.ru +21 -0
  39. data/examples/render-webdav.rb +26 -0
  40. data/examples/rest-run.rb +3 -0
  41. data/examples/rest.rb +1 -1
  42. data/examples/rest.ru +3 -0
  43. data/lib/capcode.rb +196 -100
  44. data/lib/capcode/helpers/auth.rb +130 -0
  45. data/lib/capcode/render/webdav.rb +45 -0
  46. data/lib/capcode/version.rb +1 -1
  47. metadata +45 -3
@@ -0,0 +1,3 @@
1
+ require 'rest'
2
+
3
+ run Capcode.run()
@@ -74,4 +74,4 @@ module Capcode::Views
74
74
 
75
75
  end
76
76
 
77
- Capcode.run( )
77
+ #Capcode.run( )
@@ -0,0 +1,3 @@
1
+ require 'rest'
2
+
3
+ run Capcode.application()
@@ -7,11 +7,13 @@ require 'irb'
7
7
  require 'mime/types'
8
8
  require 'capcode/version'
9
9
  require 'capcode/core_ext'
10
+ require 'capcode/helpers/auth'
10
11
  require 'capcode/render/text'
11
12
 
12
13
  module Capcode
13
14
  @@__ROUTES = {}
14
15
  @@__STATIC_DIR = nil
16
+ @@__APP = nil
15
17
 
16
18
  # @@__FILTERS = []
17
19
  # def self.before_filter( opts, &b )
@@ -51,9 +53,13 @@ module Capcode
51
53
  # * :json => MyObject : this suppose that's MyObject respond to .to_json
52
54
  # * :static => "my_file.xxx" : this suppose that's my_file.xxx exist in the static directory
53
55
  # * :xml => :my_func : :my_func must be defined in Capcode::Views
56
+ # * :webdav => /path/to/root
54
57
  #
55
58
  # If you want to use a specific layout, you can specify it with option
56
59
  # :layout
60
+ #
61
+ # If you use the WebDav renderer, you can use the option
62
+ # :resource_class (see http://github.com/georgi/rack_dav for more informations)
57
63
  def render( hash )
58
64
  if hash.class == Hash
59
65
  render_type = nil
@@ -167,7 +173,7 @@ module Capcode
167
173
  path = klass
168
174
  end
169
175
 
170
- path+((a.size>0)?("/"+a.join("/")):(""))
176
+ (ENV['RACK_BASE_URI']||'')+path+((a.size>0)?("/"+a.join("/")):(""))
171
177
  end
172
178
 
173
179
  # Calling content_for stores a block of markup in an identifier.
@@ -218,6 +224,8 @@ module Capcode
218
224
  :path => File.expand_path( File.join(".", Capcode.static ) )
219
225
  }
220
226
  end
227
+
228
+ include Authorization
221
229
  end
222
230
 
223
231
  include Rack
@@ -254,7 +262,7 @@ module Capcode
254
262
  end
255
263
 
256
264
  class << self
257
- attr :__args__, true
265
+ attr :__auth__, true
258
266
 
259
267
  # Add routes to a controller class
260
268
  #
@@ -348,55 +356,86 @@ module Capcode
348
356
  # puts "call #{proc} for #{__k}"
349
357
  # end
350
358
 
351
- r = case @env["REQUEST_METHOD"]
352
- when "GET"
353
- finalPath = nil
354
- finalArgs = nil
355
- finalNArgs = nil
359
+ # Check authz
360
+ authz_options = nil
361
+ if Capcode.__auth__.size > 0
362
+ authz_options = Capcode.__auth__[@request.path]||nil
363
+ if authz_options.nil?
364
+ route = nil
356
365
 
357
- aPath = @request.path.gsub( /^\//, "" ).split( "/" )
358
- self.class.__urls__[0].each do |p, r|
359
- xPath = p.gsub( /^\//, "" ).split( "/" )
360
- if (xPath - aPath).size == 0
361
- diffArgs = aPath - xPath
362
- diffNArgs = diffArgs.size
363
- if finalNArgs.nil? or finalNArgs > diffNArgs
364
- finalPath = p
365
- finalNArgs = diffNArgs
366
- finalArgs = diffArgs
366
+ Capcode.__auth__.each do |r, o|
367
+ regexp = "^#{r.gsub(/\/$/, "")}([/]{1}.*)?$"
368
+ if Regexp.new(regexp).match( @request.path )
369
+ if route.nil? or r.size > route.size
370
+ route = r
371
+ authz_options = o
367
372
  end
368
- end
369
-
373
+ end
370
374
  end
375
+ end
376
+ end
371
377
 
372
- nargs = self.class.__urls__[1]
373
- regexp = Regexp.new( self.class.__urls__[0][finalPath] )
374
- args = regexp.match( Rack::Utils.unescape(@request.path).gsub( Regexp.new( "^#{finalPath}" ), "" ).gsub( /^\//, "" ) )
375
- if args.nil?
376
- raise Capcode::ParameterError, "Path info `#{@request.path_info}' does not match route regexp `#{regexp.source}'"
378
+ r = catch(:halt) {
379
+ unless authz_options.nil?
380
+ http_authentication( :type => authz_options[:type], :realm => authz_options[:realm], :opaque => authz_options[:realm] ) {
381
+ authz_options[:autz]
382
+ }
383
+ end
384
+
385
+ case @env["REQUEST_METHOD"]
386
+ when "GET"
387
+ finalPath = nil
388
+ finalArgs = nil
389
+ finalNArgs = nil
390
+
391
+ aPath = @request.path.gsub( /^\//, "" ).split( "/" )
392
+ self.class.__urls__[0].each do |p, r|
393
+ xPath = p.gsub( /^\//, "" ).split( "/" )
394
+ if (xPath - aPath).size == 0
395
+ diffArgs = aPath - xPath
396
+ diffNArgs = diffArgs.size
397
+ if finalNArgs.nil? or finalNArgs > diffNArgs
398
+ finalPath = p
399
+ finalNArgs = diffNArgs
400
+ finalArgs = diffArgs
401
+ end
402
+ end
403
+
404
+ end
405
+
406
+ nargs = self.class.__urls__[1]
407
+ regexp = Regexp.new( self.class.__urls__[0][finalPath] )
408
+ args = regexp.match( Rack::Utils.unescape(@request.path).gsub( Regexp.new( "^#{finalPath}" ), "" ).gsub( /^\//, "" ) )
409
+ if args.nil?
410
+ raise Capcode::ParameterError, "Path info `#{@request.path_info}' does not match route regexp `#{regexp.source}'"
411
+ else
412
+ args = args.captures.map { |x| (x.size == 0)?nil:x }
413
+ end
414
+
415
+ while args.size < nargs
416
+ args << nil
417
+ end
418
+
419
+ get( *args )
420
+ when "POST"
421
+ _method = params.delete( "_method" ) { |_| "post" }
422
+ send( _method.downcase.to_sym )
377
423
  else
378
- args = args.captures.map { |x| (x.size == 0)?nil:x }
379
- end
380
-
381
- while args.size < nargs
382
- args << nil
383
- end
384
-
385
- get( *args )
386
- when "POST"
387
- _method = params.delete( "_method" ) { |_| "post" }
388
- send( _method.downcase.to_sym )
389
- end
424
+ _method = @env["REQUEST_METHOD"]
425
+ send( _method.downcase.to_sym )
426
+ end
427
+ }
390
428
  if r.respond_to?(:to_ary)
391
- @response.status = r[0]
392
- r[1].each do |k,v|
429
+ @response.status = r.shift #r[0]
430
+ #r[1].each do |k,v|
431
+ r.shift.each do |k,v|
393
432
  @response[k] = v
394
433
  end
395
- @response.body = r[2]
434
+ @response.body = r.shift #r[2]
396
435
  else
397
436
  @response.write r
398
437
  end
399
-
438
+
400
439
  @response.finish
401
440
  end
402
441
 
@@ -413,26 +452,46 @@ module Capcode
413
452
  def map( route, &b )
414
453
  @@__ROUTES[route] = yield
415
454
  end
416
-
417
- # Start your application.
455
+
456
+ # Allow you to add and HTTP Authentication (Basic or Digest) to controllers for or specific route
418
457
  #
419
458
  # Options :
420
- # * <tt>:port</tt> = Listen port
421
- # * <tt>:host</tt> = Listen host
422
- # * <tt>:server</tt> = Server type (webrick or mongrel)
423
- # * <tt>:log</tt> = Output logfile (default: STDOUT)
424
- # * <tt>:session</tt> = Session parameters. See Rack::Session for more informations
425
- # * <tt>:pid</tt> = PID file (default: $0.pid)
426
- # * <tt>:daemonize</tt> = Daemonize application (default: false)
427
- # * <tt>:db_config</tt> = database configuration file (default: database.yml)
428
- # * <tt>:static</tt> = Static directory (default: none -- relative to the working directory)
429
- # * <tt>:root</tt> = Root directory (default: directory of the main.rb) -- This is also the working directory !
430
- def run( args = {} )
431
- __VERBOSE = false
459
+ # * <tt>:type</tt> : Authentication type (<tt>:basic</tt> or <tt>:digest</tt>) - default : <tt>:basic</tt>
460
+ # * <tt>:realm</tt> : realm ;) - default : "Capcode.app"
461
+ # * <tt>:opaque</tt> : Your secret passphrase. You MUST set it if you use Digest Auth - default : "opaque"
462
+ # * <tt>:routes</tt> : Routes - default : "/"
463
+ #
464
+ # The block must return a Hash of username => password like that :
465
+ # {
466
+ # "user1" => "pass1",
467
+ # "user2" => "pass2",
468
+ # # ...
469
+ # }
470
+ def http_authentication( opts = {}, &b )
471
+ options = {
472
+ :type => :basic,
473
+ :realm => "Capcode.app",
474
+ :opaque => "opaque",
475
+ :routes => "/"
476
+ }.merge( opts )
477
+
478
+ options[:autz] = b.call()
479
+
480
+ @__auth__ ||= {}
432
481
 
433
- conf = {
482
+ if options[:routes].class == Array
483
+ options[:routes].each do |r|
484
+ @__auth__[r] = options
485
+ end
486
+ else
487
+ @__auth__[options[:routes]] = options
488
+ end
489
+ end
490
+
491
+ def configuration( args = {} ) #:nodoc:
492
+ {
434
493
  :port => args[:port]||3000,
435
- :host => args[:host]||"localhost",
494
+ :host => args[:host]||"0.0.0.0",
436
495
  :server => args[:server]||nil,
437
496
  :log => args[:log]||$stdout,
438
497
  :session => args[:session]||{},
@@ -441,9 +500,82 @@ module Capcode
441
500
  :db_config => File.expand_path(args[:db_config]||"database.yml"),
442
501
  :static => args[:static]||nil,
443
502
  :root => args[:root]||File.expand_path(File.dirname($0)),
444
-
503
+ :static => args[:static]||args[:root]||File.expand_path(File.dirname($0)),
504
+ :verbose => args[:verbose]||false,
445
505
  :console => false
446
506
  }
507
+ end
508
+
509
+ # Return the Rack App.
510
+ #
511
+ # Options : same has Capcode.run
512
+ def application( args = {} )
513
+ conf = configuration(args)
514
+
515
+ Capcode.constants.each do |k|
516
+ begin
517
+ if eval "Capcode::#{k}.public_methods(true).include?( '__urls__' )"
518
+ hash_of_routes, max_captures_for_routes, klass = eval "Capcode::#{k}.__urls__"
519
+ hash_of_routes.keys.each do |current_route_path|
520
+ raise Capcode::RouteError, "Route `#{current_route_path}' already define !", caller if @@__ROUTES.keys.include?(current_route_path)
521
+ @@__ROUTES[current_route_path] = klass.new
522
+ end
523
+ end
524
+ rescue => e
525
+ raise e.message
526
+ end
527
+ end
528
+
529
+ # Set Static directory
530
+ @@__STATIC_DIR = (conf[:static][0].chr == "/")?conf[:static]:"/"+conf[:static] unless conf[:static].nil?
531
+
532
+ # Initialize Rack App
533
+ puts "** Map routes." if conf[:verbose]
534
+ app = Rack::URLMap.new(@@__ROUTES)
535
+ puts "** Initialize static directory (#{conf[:static]})" if conf[:verbose]
536
+ app = Rack::Static.new(
537
+ app,
538
+ :urls => [@@__STATIC_DIR],
539
+ :root => File.expand_path(conf[:root])
540
+ ) unless conf[:static].nil?
541
+ puts "** Initialize session" if conf[:verbose]
542
+ app = Rack::Session::Cookie.new( app, conf[:session] )
543
+ app = Capcode::HTTPError.new(app)
544
+ app = Rack::ContentLength.new(app)
545
+ app = Rack::Lint.new(app)
546
+ app = Rack::ShowExceptions.new(app)
547
+ #app = Rack::Reloader.new(app) ## -- NE RELOAD QUE capcode.rb -- So !!!
548
+ # app = Rack::CommonLogger.new( app, Logger.new(conf[:log]) )
549
+
550
+ # Start database
551
+ if self.methods.include? "db_connect"
552
+ db_connect( conf[:db_config], conf[:log] )
553
+ end
554
+
555
+ if block_given?
556
+ yield( self )
557
+ end
558
+
559
+ return app
560
+ end
561
+
562
+ # Start your application.
563
+ #
564
+ # Options :
565
+ # * <tt>:port</tt> = Listen port (default: 3000)
566
+ # * <tt>:host</tt> = Listen host (default: 0.0.0.0)
567
+ # * <tt>:server</tt> = Server type (webrick or mongrel)
568
+ # * <tt>:log</tt> = Output logfile (default: STDOUT)
569
+ # * <tt>:session</tt> = Session parameters. See Rack::Session for more informations
570
+ # * <tt>:pid</tt> = PID file (default: $0.pid)
571
+ # * <tt>:daemonize</tt> = Daemonize application (default: false)
572
+ # * <tt>:db_config</tt> = database configuration file (default: database.yml)
573
+ # * <tt>:static</tt> = Static directory (default: none -- relative to the working directory)
574
+ # * <tt>:root</tt> = Root directory (default: directory of the main.rb) -- This is also the working directory !
575
+ # * <tt>:verbose</tt> = run in verbose mode
576
+ # * <tt>:auth</tt> = HTTP Basic Authentication options
577
+ def run( args = {} )
578
+ conf = configuration(args)
447
579
 
448
580
  # Parse options
449
581
  opts = OptionParser.new do |opts|
@@ -482,7 +614,7 @@ module Capcode
482
614
  exit
483
615
  end
484
616
  opts.on_tail( "-V", "--verbose", "Run in verbose mode" ) do
485
- __VERBOSE = true
617
+ conf[:verbose] = true
486
618
  end
487
619
  end
488
620
 
@@ -495,7 +627,7 @@ module Capcode
495
627
  end
496
628
 
497
629
  # Run in the Working directory
498
- puts "** Go on root directory (#{File.expand_path(conf[:root])})" if __VERBOSE
630
+ puts "** Go on root directory (#{File.expand_path(conf[:root])})" if conf[:verbose]
499
631
  Dir.chdir( conf[:root] ) do
500
632
 
501
633
  # Check that mongrel exists
@@ -508,41 +640,6 @@ module Capcode
508
640
  conf[:server] = "webrick"
509
641
  end
510
642
  end
511
-
512
- Capcode.constants.each do |k|
513
- begin
514
- if eval "Capcode::#{k}.public_methods(true).include?( '__urls__' )"
515
- hash_of_routes, max_captures_for_routes, klass = eval "Capcode::#{k}.__urls__"
516
- hash_of_routes.keys.each do |current_route_path|
517
- raise Capcode::RouteError, "Route `#{current_route_path}' already define !", caller if @@__ROUTES.keys.include?(current_route_path)
518
- @@__ROUTES[current_route_path] = klass.new
519
- end
520
- end
521
- rescue => e
522
- raise e.message
523
- end
524
- end
525
-
526
- # Set Static directory
527
- @@__STATIC_DIR = (conf[:static][0].chr == "/")?conf[:static]:"/"+conf[:static] unless conf[:static].nil?
528
-
529
- # Initialize Rack App
530
- puts "** Map routes." if __VERBOSE
531
- app = Rack::URLMap.new(@@__ROUTES)
532
- puts "** Initialize static directory (#{conf[:static]})" if __VERBOSE
533
- app = Rack::Static.new(
534
- app,
535
- :urls => [@@__STATIC_DIR],
536
- :root => File.expand_path(conf[:root])
537
- ) unless conf[:static].nil?
538
- puts "** Initialize session" if __VERBOSE
539
- app = Rack::Session::Cookie.new( app, conf[:session] )
540
- app = Capcode::HTTPError.new(app)
541
- app = Rack::ContentLength.new(app)
542
- app = Rack::Lint.new(app)
543
- app = Rack::ShowExceptions.new(app)
544
- # app = Rack::Reloader.new(app) ## -- NE RELOAD QUE capcode.rb -- So !!!
545
- app = Rack::CommonLogger.new( app, Logger.new(conf[:log]) )
546
643
 
547
644
  # From rackup !!!
548
645
  if conf[:daemonize]
@@ -567,14 +664,13 @@ module Capcode
567
664
  at_exit { File.delete(conf[:pid]) if File.exist?(conf[:pid]) }
568
665
  end
569
666
 
570
- # Start database
571
- if self.methods.include? "db_connect"
572
- db_connect( conf[:db_config], conf[:log] )
573
- end
574
-
667
+ app = nil
575
668
  if block_given?
576
- yield( self )
669
+ app = application(conf) { yield( self ) }
670
+ else
671
+ app = application(conf)
577
672
  end
673
+ app = Rack::CommonLogger.new( app, Logger.new(conf[:log]) )
578
674
 
579
675
  if conf[:console]
580
676
  puts "Run console..."
@@ -0,0 +1,130 @@
1
+ # Because this helper was trully inspired by this post :
2
+ # http://www.gittr.com/index.php/archive/sinatra-basic-authentication-selectively-applied/
3
+ # and because the code in this post was extracted out of Wink, this file follow the
4
+ # Wink license :
5
+ #
6
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
7
+ # of this software and associated documentation files (the "Software"), to deal
8
+ # in the Software without restriction, including without limitation the rights
9
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
+ # copies of the Software, and to permit persons to whom the Software is
11
+ # furnished to do so, subject to the following conditions:
12
+ #
13
+ # The above copyright notice and this permission notice shall be included in
14
+ # all copies or substantial portions of the Software.
15
+ #
16
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
+ # THE SOFTWARE.
23
+
24
+ module Capcode
25
+ module Helpers
26
+ module Authorization
27
+ def auth #:nodoc:
28
+ if @auth_type == :basic
29
+ @auth ||= Rack::Auth::Basic::Request.new(env)
30
+ else
31
+ @auth ||= Rack::Auth::Digest::Request.new(env)
32
+ end
33
+ end
34
+
35
+ def basic_unauthorized!(realm) #:nodoc:
36
+ response['WWW-Authenticate'] = %(Basic realm="#{realm}")
37
+ throw :halt, [ 401, {}, 'Authorization Required' ]
38
+ end
39
+
40
+ def digest_unauthorized!(realm, opaque) #:nodoc:
41
+ response['WWW-Authenticate'] = %(Digest realm="#{realm}", qop="auth", nonce="#{Rack::Auth::Digest::Nonce.new.to_s}", opaque="#{H(opaque)}")
42
+ throw :halt, [ 401, {}, 'Authorization Required' ]
43
+ end
44
+
45
+ def H(data) #:nodoc:
46
+ ::Digest::MD5.hexdigest(data)
47
+ end
48
+
49
+ def KD(secret, data) #:nodoc:
50
+ H([secret, data] * ':')
51
+ end
52
+
53
+ def A1(auth, password) #:nodoc:
54
+ [ auth.username, auth.realm, password ] * ':'
55
+ end
56
+
57
+ def A2(auth) #:nodoc:
58
+ [ auth.method, auth.uri ] * ':'
59
+ end
60
+
61
+ def digest(auth, password) #:nodoc:
62
+ password_hash = H(A1(auth, password))
63
+
64
+ KD(password_hash, [ auth.nonce, auth.nc, auth.cnonce, "auth", H(A2(auth)) ] * ':')
65
+ end
66
+
67
+ def digest_authorize( ) #:nodoc:
68
+ h = @authorizations.call( )
69
+
70
+ user = auth.username
71
+ pass = h[user]||false
72
+
73
+ (pass && (digest(auth, pass) == auth.response))
74
+ end
75
+
76
+ def basic_authorize(username, password) #:nodoc:
77
+ h = @authorizations.call( )
78
+
79
+ user = username
80
+ pass = h[user]||false
81
+
82
+ (pass == password)
83
+ end
84
+
85
+ def bad_request! #:nodoc:
86
+ throw :halt, [ 400, {}, 'Bad Request' ]
87
+ end
88
+
89
+ def authorized? #:nodoc:
90
+ request.env['REMOTE_USER']
91
+ end
92
+
93
+ # Allow you to add and HTTP Authentication (Basic or Digest) to a controller
94
+ #
95
+ # Options :
96
+ # * <tt>:type</tt> : Authentication type (<tt>:basic</tt> or <tt>:digest</tt>) - default : <tt>:basic</tt>
97
+ # * <tt>:realm</tt> : realm ;) - default : "Capcode.app"
98
+ # * <tt>:opaque</tt> : Your secret passphrase. You MUST set it if you use Digest Auth - default : "opaque"
99
+ #
100
+ # The block must return a Hash of username => password like that :
101
+ # {
102
+ # "user1" => "pass1",
103
+ # "user2" => "pass2",
104
+ # # ...
105
+ # }
106
+ def http_authentication( opts = {}, &b )
107
+ @auth = nil
108
+
109
+ @auth_type = opts[:type]||:basic
110
+ realm = opts[:realm]||"Capcode.app"
111
+ opaque = opts[:opaque]||"opaque"
112
+ @authorizations = b
113
+
114
+ return if authorized?
115
+
116
+ if @auth_type == :basic
117
+ basic_unauthorized!(realm) unless auth.provided?
118
+ bad_request! unless auth.basic?
119
+ basic_unauthorized!(realm) unless basic_authorize(*auth.credentials)
120
+ else
121
+ digest_unauthorized!(realm, opaque) unless auth.provided?
122
+ bad_request! unless auth.digest?
123
+ digest_unauthorized!(realm, opaque) unless digest_authorize
124
+ end
125
+
126
+ request.env['REMOTE_USER'] = auth.username
127
+ end
128
+ end
129
+ end
130
+ end