otto 2.0.0.pre8 → 2.0.0.pre10

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 (53) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +1 -1
  3. data/.github/workflows/claude-code-review.yml +1 -1
  4. data/.github/workflows/claude.yml +1 -1
  5. data/.github/workflows/code-smells.yml +2 -2
  6. data/CHANGELOG.rst +73 -35
  7. data/CLAUDE.md +44 -0
  8. data/Gemfile.lock +7 -7
  9. data/README.md +20 -0
  10. data/docs/.gitignore +2 -0
  11. data/docs/modern-authentication-authorization-landscape.md +558 -0
  12. data/docs/multi-strategy-authentication-design.md +1401 -0
  13. data/lib/otto/core/error_handler.rb +104 -10
  14. data/lib/otto/core/freezable.rb +0 -2
  15. data/lib/otto/core/helper_registry.rb +135 -0
  16. data/lib/otto/core/lifecycle_hooks.rb +63 -0
  17. data/lib/otto/core/middleware_management.rb +70 -0
  18. data/lib/otto/core/middleware_stack.rb +12 -8
  19. data/lib/otto/core/router.rb +25 -31
  20. data/lib/otto/core.rb +3 -0
  21. data/lib/otto/errors.rb +92 -0
  22. data/lib/otto/helpers.rb +2 -2
  23. data/lib/otto/locale/middleware.rb +1 -1
  24. data/lib/otto/mcp/core.rb +33 -0
  25. data/lib/otto/mcp/protocol.rb +1 -1
  26. data/lib/otto/mcp/rate_limiting.rb +6 -2
  27. data/lib/otto/mcp/schema_validation.rb +2 -2
  28. data/lib/otto/mcp.rb +1 -0
  29. data/lib/otto/privacy/core.rb +82 -0
  30. data/lib/otto/privacy.rb +1 -0
  31. data/lib/otto/{helpers/request.rb → request.rb} +17 -6
  32. data/lib/otto/{helpers/response.rb → response.rb} +20 -7
  33. data/lib/otto/response_handlers/json.rb +1 -3
  34. data/lib/otto/response_handlers/view.rb +1 -1
  35. data/lib/otto/route.rb +2 -4
  36. data/lib/otto/route_handlers/base.rb +88 -5
  37. data/lib/otto/route_handlers/class_method.rb +9 -67
  38. data/lib/otto/route_handlers/instance_method.rb +10 -57
  39. data/lib/otto/route_handlers/lambda.rb +2 -2
  40. data/lib/otto/route_handlers/logic_class.rb +85 -90
  41. data/lib/otto/security/authentication/auth_strategy.rb +2 -2
  42. data/lib/otto/security/authentication/strategies/api_key_strategy.rb +1 -1
  43. data/lib/otto/security/authentication/strategy_result.rb +9 -9
  44. data/lib/otto/security/authorization_error.rb +1 -1
  45. data/lib/otto/security/config.rb +3 -3
  46. data/lib/otto/security/core.rb +167 -0
  47. data/lib/otto/security/middleware/csrf_middleware.rb +1 -1
  48. data/lib/otto/security/middleware/validation_middleware.rb +1 -1
  49. data/lib/otto/security/rate_limiter.rb +7 -3
  50. data/lib/otto/security.rb +1 -0
  51. data/lib/otto/version.rb +1 -1
  52. data/lib/otto.rb +29 -404
  53. metadata +12 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b094c2fc179b84631bf53b1e4b7f3cbcd661f83e1f8a88ab6c1f22d3e8b11cbd
4
- data.tar.gz: 70a7588c86b8b3f31968b577a298f05c09cad5ef5d9fcf028f0feece2963e8fb
3
+ metadata.gz: d5e7fb7a7177b6488a5a3cdacc8e3a929d58f92d44fb03bb506effca3f6c3550
4
+ data.tar.gz: 351a23a9c8e12aff50296cde108e73478dac7cf052d687deb5855f3ccb35cd79
5
5
  SHA512:
6
- metadata.gz: c9e8f2f46cf51bc0799dd6030e52a48c1665b9978efb8bc686977be722e61b3c553f17db91d9fd795447b5b4882ac95af404a71e14f9b65d3935a992ecb768f1
7
- data.tar.gz: d0c6f98edf2f468297dcb19bb9743b1a2858f2497cf40d2fc4332550a883542f2b7e1a1de8ac4b9f9d352b40c53e974d6c99aecee2679abc6b38868c02ae5bd6
6
+ metadata.gz: 651edcf79562877a2046132ccc876f747545d1bed8f4821352af4c16f8e795bec5d02e82afcf27320b368af9e25853df3c71d26c4fa6b6f81878704322d0f29d
7
+ data.tar.gz: e763f2d28e2ff4dd0d16a98ff8ab9eda6d495d32e7be10b31a1c21516a075fb1f49a44464eeaf66d92f701821ab3fce91ab72c25d18430efa70e65b2a435cb94
@@ -36,7 +36,7 @@ jobs:
36
36
  experimental: true
37
37
 
38
38
  steps:
39
- - uses: actions/checkout@v5
39
+ - uses: actions/checkout@v6
40
40
  - name: Set up Ruby
41
41
  uses: ruby/setup-ruby@v1
42
42
  continue-on-error: ${{ matrix.experimental }}
@@ -27,7 +27,7 @@ jobs:
27
27
 
28
28
  steps:
29
29
  - name: Checkout repository
30
- uses: actions/checkout@v5
30
+ uses: actions/checkout@v6
31
31
  with:
32
32
  fetch-depth: 1
33
33
 
@@ -26,7 +26,7 @@ jobs:
26
26
  actions: read # Required for Claude to read CI results on PRs
27
27
  steps:
28
28
  - name: Checkout repository
29
- uses: actions/checkout@v5
29
+ uses: actions/checkout@v6
30
30
  with:
31
31
  fetch-depth: 1
32
32
 
@@ -21,7 +21,7 @@ jobs:
21
21
 
22
22
  steps:
23
23
  - name: Checkout code
24
- uses: actions/checkout@v5
24
+ uses: actions/checkout@v6
25
25
 
26
26
  - name: Set up Ruby
27
27
  uses: ruby/setup-ruby@v1
@@ -88,7 +88,7 @@ jobs:
88
88
 
89
89
  steps:
90
90
  - name: Checkout code
91
- uses: actions/checkout@v5
91
+ uses: actions/checkout@v6
92
92
 
93
93
  - name: Set up Ruby
94
94
  uses: ruby/setup-ruby@v1
data/CHANGELOG.rst CHANGED
@@ -7,71 +7,109 @@ The format is based on `Keep a Changelog <https://keepachangelog.com/en/1.1.0/>`
7
7
 
8
8
  <!--scriv-insert-here-->
9
9
 
10
- .. _changelog-2.0.0.pre8:
10
+ .. _changelog-2.0.0.pre10:
11
11
 
12
- 2.0.0.pre8 — 2025-11-27
13
- =======================
12
+ 2.0.0.pre10 — 2025-12-09
13
+ ========================
14
14
 
15
- Fixed
15
+ Added
16
16
  -----
17
17
 
18
- - Routes declaring ``response=json`` now return 401 JSON errors instead of 302 redirects when authentication fails, regardless of Accept header. The route's explicit configuration takes precedence over content negotiation.
18
+ - ``Otto::Request`` and ``Otto::Response`` classes extending Rack equivalents
19
+ - ``register_request_helpers`` and ``register_response_helpers`` for application-specific helpers
20
+ - Helper modules included at class level (not per-request extension)
19
21
 
20
- .. _changelog-2.0.0.pre7:
22
+ Changed
23
+ -------
21
24
 
22
- 2.0.0.pre7 2025-11-24
25
+ - Moved ``lib/otto/helpers/request.rb`` ``lib/otto/request.rb``
26
+ - Moved ``lib/otto/helpers/response.rb`` → ``lib/otto/response.rb``
27
+ - All internal code now uses ``Otto::Request``/``Otto::Response`` instead of ``Rack::Request``/``Rack::Response``
28
+
29
+ .. _changelog-2.0.0.pre9:
30
+
31
+ 2.0.0.pre9 — 2025-12-06
23
32
  =======================
24
33
 
25
34
  Added
26
35
  -----
27
36
 
28
- - Error handler registration system for expected business logic errors. Register handlers with ``otto.register_error_handler(ErrorClass, status: 404, log_level: :info)`` to return proper HTTP status codes and avoid logging expected errors as 500s with backtraces. Supports custom response handlers via blocks for complete control over error responses.
37
+ - Base HTTP error classes (``Otto::NotFoundError``, ``Otto::BadRequestError``, ``Otto::ForbiddenError``, ``Otto::UnauthorizedError``, ``Otto::PayloadTooLargeError``) that implementing projects can subclass for consistent error handling
38
+ - Auto-registration of all framework error classes during ``Otto#initialize`` - framework errors now automatically return correct HTTP status codes without manual registration
29
39
 
30
40
  Changed
31
41
  -------
32
42
 
33
- - Backtrace logging now always logs at ERROR level (was DEBUG) with sanitized file paths for security. Backtraces for unhandled 500 errors are always logged regardless of ``OTTO_DEBUG`` setting, with paths sanitized to prevent exposing system information (project files show relative paths, gems show ``[GEM] name-version/path``, Ruby stdlib shows ``[RUBY] filename``).
34
- - Increased backtrace limit from 10 to 20 lines for critical errors to provide better debugging context.
43
+ - Framework error classes now inherit from new base classes: ``Otto::Security::AuthorizationError`` < ``Otto::ForbiddenError``, ``Otto::Security::CSRFError`` < ``Otto::ForbiddenError``, ``Otto::Security::RequestTooLargeError`` < ``Otto::PayloadTooLargeError``, ``Otto::Security::ValidationError`` < ``Otto::BadRequestError``, ``Otto::MCP::ValidationError`` < ``Otto::BadRequestError``
44
+ - ``Otto::Security::RequestTooLargeError`` now returns HTTP 413 (Payload Too Large) instead of 500, semantically correct per RFC 7231
45
+
46
+ - Consolidated route handler implementation using Template Method pattern, reducing duplication by ~120 lines while improving maintainability
47
+
48
+ Fixed
49
+ -----
50
+
51
+ - Error handlers now respect route's ``response=json`` parameter for content
52
+ negotiation, ensuring API routes always return JSON error responses regardless
53
+ of the Accept header.
54
+
55
+ - Rate limiters now respect route ``response=json`` declarations when returning
56
+ throttled responses, matching the error handler fix for consistent content
57
+ negotiation across all error paths.
58
+
59
+ - ClassMethodHandler direct testing context now respects route ``response_type``
60
+ when generating error responses.
61
+
62
+ - Unified error handling across ClassMethodHandler and InstanceMethodHandler to consistently support JSON content negotiation
35
63
 
36
64
  AI Assistance
37
65
  -------------
38
66
 
39
- - Implemented error handler registration architecture with comprehensive test coverage (17 test cases) using sequential thinking to work through security implications and design decisions. AI assisted with path sanitization strategy, error classification patterns, and ensuring backward compatibility with existing error handling.
67
+ - Implementation design and architecture developed with AI pair programming
68
+ - Comprehensive test coverage (31 new base class tests, 12 auto-registration tests) developed with AI assistance
69
+ - Error class hierarchy and inheritance patterns refined through AI-guided architectural discussion
40
70
 
41
- Improved backtrace sanitization security and readability
42
- --------------------------------------------------------
71
+ .. _changelog-2.0.0.pre8:
43
72
 
44
- **Security Enhancements:**
73
+ 2.0.0.pre8 — 2025-11-27
74
+ =======================
45
75
 
46
- - Fixed bundler gem path detection to correctly sanitize git-based gems
47
- - Now properly handles nested gem paths like ``/gems/3.4.0/bundler/gems/otto-abc123/``
48
- - Strips git hash suffixes from bundler gems (``otto-abc123def456`` → ``otto``)
49
- - Removes version numbers from regular gems (``rack-3.2.4`` ``rack``)
50
- - Prevents exposure of absolute paths, usernames, and project names in logs
76
+ Fixed
77
+ -----
78
+
79
+ - Routes declaring ``response=json`` now return 401 JSON errors instead of 302 redirects when authentication fails, regardless of Accept header. The route's explicit configuration takes precedence over content negotiation.
80
+
81
+ .. _changelog-2.0.0.pre7:
82
+
83
+ 2.0.0.pre7 — 2025-11-24
84
+ =======================
85
+
86
+ Added
87
+ -----
88
+
89
+ - Error handler registration system for expected business logic errors via ``otto.register_error_handler(ErrorClass, status:, log_level:)``. Supports custom response handlers via blocks.
51
90
 
52
- **Improvements:**
91
+ Changed
92
+ -------
93
+
94
+ - Backtrace logging now always logs at ERROR level with sanitized file paths (was DEBUG level with full paths)
95
+ - Increased backtrace limit from 10 to 20 lines for better debugging context
96
+ - Improved gem path formatting in backtraces (e.g., ``[GEM] rack/lib/rack.rb:20``)
97
+
98
+ Fixed
99
+ -----
53
100
 
54
- - Bundler gems now show as ``[GEM] otto/lib/otto/route.rb:142`` instead of ``[GEM] 3.4.0/bundler/gems/...``
55
- - Regular gems show cleaner output: ``[GEM] rack/lib/rack.rb:20`` instead of ``[GEM] rack-3.2.4/lib/rack.rb:20``
56
- - Multi-hyphenated gem names handled correctly (``active-record-import-1.5.0`` → ``active-record-import``)
57
- - Better handling of version-only directory names in gem paths
101
+ - Fixed path sanitization for bundler git-based gems and multi-hyphenated gem names
58
102
 
59
- **Documentation:**
103
+ Documentation
104
+ -------------
60
105
 
61
- - Added comprehensive backtrace sanitization section to CLAUDE.md
62
106
  - Documented security guarantees and sanitization rules
63
107
  - Added examples showing before/after path transformations
64
- - Created comprehensive test suite for backtrace sanitization
65
-
66
- **Rationale:**
67
108
 
68
- Raw backtraces expose sensitive information:
69
- - Usernames (``/Users/alice/``, ``/home/admin/``)
70
- - Project structure and internal organization
71
- - Gem installation paths and Ruby versions
72
- - System architecture details
109
+ AI Assistance
110
+ -------------
73
111
 
74
- This improvement ensures all backtraces are sanitized automatically, preventing accidental leakage of sensitive system information while maintaining readability for debugging.
112
+ - Implemented error handler registration architecture with comprehensive test coverage (17 test cases) using sequential thinking to work through security implications and design decisions. AI assisted with path sanitization strategy, error classification patterns, and ensuring backward compatibility with existing error handling.
75
113
 
76
114
  .. _changelog-2.0.0.pre6:
77
115
 
data/CLAUDE.md CHANGED
@@ -14,6 +14,40 @@ otto.register_error_handler(YourApp::RateLimited, status: 429, log_level: :warn)
14
14
 
15
15
  Must be registered before first request (before configuration freezing).
16
16
 
17
+ ## Request/Response Helper Registration
18
+
19
+ Register application helpers that integrate with Otto features (authentication, privacy, locale):
20
+
21
+ ```ruby
22
+ module YourApp::RequestHelpers
23
+ def current_customer
24
+ user = strategy_result&.user
25
+ user.is_a?(YourApp::Customer) ? user : YourApp::Customer.anonymous
26
+ end
27
+ end
28
+
29
+ otto = Otto.new('routes.txt')
30
+ otto.register_request_helpers(YourApp::RequestHelpers)
31
+ otto.register_response_helpers(YourApp::ResponseHelpers)
32
+ ```
33
+
34
+ Must be registered before first request.
35
+
36
+ > [!NOTE]
37
+ > **Helper availability by context:** The base `Otto::Request` and `Otto::Response` classes (with Otto's built-in helpers like `masked_ip`, `geo_country`) are available everywhere, including Rack middleware. However, custom helpers registered via `register_request_helpers` and `register_response_helpers` are only available in route handlers, error handlers, and components that have access to the Otto instance's dynamic request/response classes.
38
+
39
+ ### Reserved Method Names
40
+
41
+ Helper modules should avoid overriding these methods inherited from Rack::Request/Rack::Response:
42
+
43
+ **Request reserved methods**: `env`, `params`, `cookies`, `session`, `path`, `path_info`, `query_string`, `request_method`, `content_type`, `content_length`, `media_type`, `get?`, `post?`, `put?`, `delete?`, `head?`, `options?`, `patch?`, `xhr?`, `referer`, `user_agent`, `base_url`, `url`, `fullpath`, `ip`, `host`, `port`, `ssl?`, `scheme`
44
+
45
+ **Response reserved methods**: `status`, `headers`, `body`, `finish`, `write`, `close`, `set_cookie`, `delete_cookie`, `redirect`, `content_type`, `content_length`, `location`
46
+
47
+ **Otto-specific methods**: `request` (on Response), `app_path`, `masked_ip`, `hashed_ip`, `client_ipaddress`, `secure?`, `local?`, `ajax?`
48
+
49
+ No runtime validation is performed for performance reasons. Overriding these methods will cause undefined behavior.
50
+
17
51
  ## Authentication Architecture
18
52
 
19
53
  Authentication is handled by `RouteAuthWrapper` at the handler level, NOT by middleware.
@@ -115,6 +149,16 @@ bundle exec rubocop
115
149
  bundle exec rspec
116
150
  ```
117
151
 
152
+ ## Git Commands
153
+
154
+ Always use `--no-pager` with git commands to avoid interactive pager issues:
155
+
156
+ ```bash
157
+ git --no-pager diff
158
+ git --no-pager log
159
+ git --no-pager show
160
+ ```
161
+
118
162
  ## Key Architecture Principles
119
163
 
120
164
  - **Security by Default**: IP privacy, configuration freezing, backtrace sanitization
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- otto (2.0.0.pre8)
4
+ otto (2.0.0.pre10)
5
5
  concurrent-ruby (~> 1.3, < 2.0)
6
6
  facets (~> 3.1)
7
7
  ipaddr (~> 1, < 2.0)
@@ -62,7 +62,7 @@ GEM
62
62
  pp (>= 0.6.0)
63
63
  rdoc (>= 4.0.0)
64
64
  reline (>= 0.4.2)
65
- json (2.15.2)
65
+ json (2.16.0)
66
66
  json_schemer (2.4.0)
67
67
  bigdecimal
68
68
  hana (~> 1.3)
@@ -113,7 +113,7 @@ GEM
113
113
  rack
114
114
  rack-test (2.2.0)
115
115
  rack (>= 1.3)
116
- rackup (2.2.1)
116
+ rackup (2.3.1)
117
117
  rack (>= 3)
118
118
  rainbow (3.1.1)
119
119
  rbs (3.9.5)
@@ -156,21 +156,21 @@ GEM
156
156
  rubocop-ast (>= 1.47.1, < 2.0)
157
157
  ruby-progressbar (~> 1.7)
158
158
  unicode-display_width (>= 2.4.0, < 4.0)
159
- rubocop-ast (1.47.1)
159
+ rubocop-ast (1.48.0)
160
160
  parser (>= 3.3.7.2)
161
161
  prism (~> 1.4)
162
162
  rubocop-performance (1.26.1)
163
163
  lint_roller (~> 1.1)
164
164
  rubocop (>= 1.75.0, < 2.0)
165
165
  rubocop-ast (>= 1.47.1, < 2.0)
166
- rubocop-rspec (3.7.0)
166
+ rubocop-rspec (3.8.0)
167
167
  lint_roller (~> 1.1)
168
- rubocop (~> 1.72, >= 1.72.1)
168
+ rubocop (~> 1.81)
169
169
  rubocop-thread_safety (0.7.3)
170
170
  lint_roller (~> 1.1)
171
171
  rubocop (~> 1.72, >= 1.72.1)
172
172
  rubocop-ast (>= 1.44.0, < 2.0)
173
- ruby-lsp (0.26.2)
173
+ ruby-lsp (0.26.4)
174
174
  language_server-protocol (~> 3.17.0)
175
175
  prism (>= 1.2, < 2.0)
176
176
  rbs (>= 3, < 5)
data/README.md CHANGED
@@ -84,6 +84,26 @@ app = Otto.new("./routes", {
84
84
 
85
85
  Security features include CSRF protection, input validation, security headers, and trusted proxy configuration.
86
86
 
87
+ ## Error Handling
88
+
89
+ Otto provides base error classes that automatically return correct HTTP status codes:
90
+
91
+ ```ruby
92
+ # Use built-in error classes directly
93
+ raise Otto::NotFoundError, "Product not found" # Returns 404
94
+ raise Otto::BadRequestError, "Invalid parameter" # Returns 400
95
+ raise Otto::UnauthorizedError, "Login required" # Returns 401
96
+ raise Otto::ForbiddenError, "Access denied" # Returns 403
97
+
98
+ # Or subclass them for your application
99
+ class MyApp::ResourceNotFound < Otto::NotFoundError; end
100
+
101
+ # Optionally customize status or logging (overrides auto-registration)
102
+ app.register_error_handler(MyApp::ResourceNotFound, status: 410, log_level: :warn)
103
+ ```
104
+
105
+ All framework errors are auto-registered during initialization. No manual registration required unless you want custom behavior.
106
+
87
107
  ## Privacy by Default
88
108
 
89
109
  Otto automatically masks public IP addresses and anonymizes user agents to comply with GDPR, CCPA, and other privacy regulations:
data/docs/.gitignore CHANGED
@@ -3,3 +3,5 @@
3
3
  !migrating/
4
4
  !migrating/*.md
5
5
  !ipaddr-encoding-quirk.md
6
+ !modern-authentication-authorization-landscape.md
7
+ !multi-strategy-authentication-design.md