Capcode 0.8.4 → 0.8.5

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 (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