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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/Gemfile +1 -1
- data/Gemfile.lock +3 -3
- data/docs/.gitignore +2 -0
- data/lib/otto/response_handlers.rb +141 -0
- data/lib/otto/route.rb +120 -54
- data/lib/otto/route_definition.rb +187 -0
- data/lib/otto/route_handlers.rb +383 -0
- data/lib/otto/security/authentication.rb +289 -0
- data/lib/otto/version.rb +1 -1
- data/lib/otto.rb +70 -3
- metadata +6 -1
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
|
98
|
+
raw = File.readlines(path).select { |line| line =~ /^\w/ }.collect { |line| line.strip }
|
91
99
|
raw.each do |entry|
|
92
|
-
|
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
|
+
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
|