otto 1.4.0 → 1.5.0

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.
data/lib/otto.rb CHANGED
@@ -8,14 +8,18 @@ require 'rack/request'
8
8
  require 'rack/response'
9
9
  require 'rack/utils'
10
10
 
11
+ require_relative 'otto/route_definition'
11
12
  require_relative 'otto/route'
12
13
  require_relative 'otto/static'
13
14
  require_relative 'otto/helpers/request'
14
15
  require_relative 'otto/helpers/response'
16
+ require_relative 'otto/response_handlers'
17
+ require_relative 'otto/route_handlers'
15
18
  require_relative 'otto/version'
16
19
  require_relative 'otto/security/config'
17
20
  require_relative 'otto/security/csrf'
18
21
  require_relative 'otto/security/validator'
22
+ require_relative 'otto/security/authentication'
19
23
 
20
24
  # Otto is a simple Rack router that allows you to define routes in a file
21
25
  # with built-in security features including CSRF protection, input validation,
@@ -56,7 +60,7 @@ class Otto
56
60
  @global_config
57
61
  end
58
62
 
59
- attr_reader :routes, :routes_literal, :routes_static, :route_definitions, :option, :static_route, :security_config, :locale_config
63
+ attr_reader :routes, :routes_literal, :routes_static, :route_definitions, :option, :static_route, :security_config, :locale_config, :auth_config, :route_handler_factory
60
64
  attr_accessor :not_found, :server_error, :middleware_stack
61
65
 
62
66
  def initialize(path = nil, opts = {})
@@ -70,6 +74,7 @@ class Otto
70
74
  }.merge(opts)
71
75
  @security_config = Otto::Security::Config.new
72
76
  @middleware_stack = []
77
+ @route_handler_factory = opts[:route_handler_factory] || Otto::RouteHandlers::HandlerFactory
73
78
 
74
79
  # Configure locale support (merge global config with instance options)
75
80
  configure_locale(opts)
@@ -77,6 +82,9 @@ class Otto
77
82
  # Configure security based on options
78
83
  configure_security(opts)
79
84
 
85
+ # Configure authentication based on options
86
+ configure_authentication(opts)
87
+
80
88
  Otto.logger.debug "new Otto: #{opts}" if Otto.debug
81
89
  load(path) unless path.nil?
82
90
  super()
@@ -87,9 +95,12 @@ class Otto
87
95
  path = File.expand_path(path)
88
96
  raise ArgumentError, "Bad path: #{path}" unless File.exist?(path)
89
97
 
90
- raw = File.readlines(path).select { |line| line =~ /^\w/ }.collect { |line| line.strip.split(/\s+/) }
98
+ raw = File.readlines(path).select { |line| line =~ /^\w/ }.collect { |line| line.strip }
91
99
  raw.each do |entry|
92
- verb, path, definition = *entry
100
+ # Enhanced parsing: split only on first two whitespace boundaries
101
+ # This preserves parameters in the definition part
102
+ parts = entry.split(/\s+/, 3)
103
+ verb, path, definition = parts[0], parts[1], parts[2]
93
104
  route = Otto::Route.new verb, path, definition
94
105
  route.otto = self
95
106
  path_clean = path.gsub(%r{/$}, '')
@@ -412,6 +423,49 @@ class Otto
412
423
  @locale_config[:default_locale] = default_locale if default_locale
413
424
  end
414
425
 
426
+ # Enable authentication middleware for route-level access control.
427
+ # This will automatically check route auth parameters and enforce authentication.
428
+ #
429
+ # @example
430
+ # otto.enable_authentication!
431
+ def enable_authentication!
432
+ return if middleware_enabled?(Otto::Security::AuthenticationMiddleware)
433
+
434
+ use Otto::Security::AuthenticationMiddleware, @auth_config
435
+ end
436
+
437
+ # Configure authentication strategies for route-level access control.
438
+ #
439
+ # @param strategies [Hash] Hash mapping strategy names to strategy instances
440
+ # @param default_strategy [String] Default strategy to use when none specified
441
+ # @example
442
+ # otto.configure_auth_strategies({
443
+ # 'publically' => Otto::Security::PublicStrategy.new,
444
+ # 'authenticated' => Otto::Security::SessionStrategy.new(session_key: 'user_id'),
445
+ # 'role:admin' => Otto::Security::RoleStrategy.new(['admin']),
446
+ # 'api_key' => Otto::Security::APIKeyStrategy.new(api_keys: ['secret123'])
447
+ # })
448
+ def configure_auth_strategies(strategies, default_strategy: 'publically')
449
+ @auth_config ||= {}
450
+ @auth_config[:auth_strategies] = strategies
451
+ @auth_config[:default_auth_strategy] = default_strategy
452
+
453
+ enable_authentication! unless strategies.empty?
454
+ end
455
+
456
+ # Add a single authentication strategy
457
+ #
458
+ # @param name [String] Strategy name
459
+ # @param strategy [Otto::Security::AuthStrategy] Strategy instance
460
+ # @example
461
+ # otto.add_auth_strategy('custom', MyCustomStrategy.new)
462
+ def add_auth_strategy(name, strategy)
463
+ @auth_config ||= { auth_strategies: {}, default_auth_strategy: 'publically' }
464
+ @auth_config[:auth_strategies][name] = strategy
465
+
466
+ enable_authentication!
467
+ end
468
+
415
469
  private
416
470
 
417
471
  def configure_locale(opts)
@@ -467,6 +521,19 @@ class Otto
467
521
  @middleware_stack.any? { |m| m == middleware_class }
468
522
  end
469
523
 
524
+ def configure_authentication(opts)
525
+ # Configure authentication strategies
526
+ @auth_config = {
527
+ auth_strategies: opts[:auth_strategies] || {},
528
+ default_auth_strategy: opts[:default_auth_strategy] || 'publically'
529
+ }
530
+
531
+ # Enable authentication middleware if strategies are configured
532
+ if opts[:auth_strategies] && !opts[:auth_strategies].empty?
533
+ enable_authentication!
534
+ end
535
+ end
536
+
470
537
  def handle_error(error, env)
471
538
  # Log error details internally but don't expose them
472
539
  error_id = SecureRandom.hex(8)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: otto
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.0
4
+ version: 1.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Delano Mandelbaum
@@ -88,6 +88,7 @@ files:
88
88
  - Gemfile.lock
89
89
  - LICENSE.txt
90
90
  - README.md
91
+ - docs/.gitignore
91
92
  - examples/basic/app.rb
92
93
  - examples/basic/config.ru
93
94
  - examples/basic/routes
@@ -105,7 +106,11 @@ files:
105
106
  - lib/otto/helpers/base.rb
106
107
  - lib/otto/helpers/request.rb
107
108
  - lib/otto/helpers/response.rb
109
+ - lib/otto/response_handlers.rb
108
110
  - lib/otto/route.rb
111
+ - lib/otto/route_definition.rb
112
+ - lib/otto/route_handlers.rb
113
+ - lib/otto/security/authentication.rb
109
114
  - lib/otto/security/config.rb
110
115
  - lib/otto/security/csrf.rb
111
116
  - lib/otto/security/validator.rb