Class: Otto

Inherits:
Object
  • Object
show all
Extended by:
ClassMethods
Includes:
CaddyTLS::Core, Core::Configuration, Core::ErrorHandler, Core::FileSafety, Core::HelperRegistry, Core::LifecycleHooks, Core::MiddlewareManagement, Core::Router, Core::StaticMounts, Core::UriGenerator, MCP::Core, Privacy::Core, Security::Core
Defined in:
lib/otto.rb,
lib/otto/route.rb,
lib/otto/utils.rb,
lib/otto/errors.rb,
lib/otto/locale.rb,
lib/otto/static.rb,
lib/otto/privacy.rb,
lib/otto/request.rb,
lib/otto/env_keys.rb,
lib/otto/mcp/core.rb,
lib/otto/response.rb,
lib/otto/mcp/errors.rb,
lib/otto/mcp/server.rb,
lib/otto/core/router.rb,
lib/otto/mcp/options.rb,
lib/otto/helpers/base.rb,
lib/otto/mcp/endpoint.rb,
lib/otto/mcp/protocol.rb,
lib/otto/mcp/registry.rb,
lib/otto/privacy/core.rb,
lib/otto/design_system.rb,
lib/otto/locale/config.rb,
lib/otto/security/core.rb,
lib/otto/security/csrf.rb,
lib/otto/caddy_tls/core.rb,
lib/otto/core/freezable.rb,
lib/otto/mcp/auth/token.rb,
lib/otto/privacy/config.rb,
lib/otto/route_handlers.rb,
lib/otto/logging_helpers.rb,
lib/otto/security/config.rb,
lib/otto/caddy_tls/server.rb,
lib/otto/core/file_safety.rb,
lib/otto/mcp/route_parser.rb,
lib/otto/route_definition.rb,
lib/otto/locale/middleware.rb,
lib/otto/mcp/rate_limiting.rb,
lib/otto/response_handlers.rb,
lib/otto/core/configuration.rb,
lib/otto/core/error_handler.rb,
lib/otto/core/static_mounts.rb,
lib/otto/core/uri_generator.rb,
lib/otto/helpers/validation.rb,
lib/otto/privacy/ip_privacy.rb,
lib/otto/security/csp/nonce.rb,
lib/otto/security/validator.rb,
lib/otto/optional_dependency.rb,
lib/otto/route_handlers/base.rb,
lib/otto/security/csp/parser.rb,
lib/otto/security/csp/policy.rb,
lib/otto/security/csp/report.rb,
lib/otto/security/csp/writer.rb,
lib/otto/core/helper_registry.rb,
lib/otto/core/lifecycle_hooks.rb,
lib/otto/privacy/asn_resolver.rb,
lib/otto/privacy/geo_resolver.rb,
lib/otto/core/middleware_stack.rb,
lib/otto/mcp/schema_validation.rb,
lib/otto/route_handlers/lambda.rb,
lib/otto/security/configurator.rb,
lib/otto/security/rate_limiter.rb,
lib/otto/response_handlers/auto.rb,
lib/otto/response_handlers/base.rb,
lib/otto/response_handlers/json.rb,
lib/otto/response_handlers/view.rb,
lib/otto/route_handlers/factory.rb,
lib/otto/security/rate_limiting.rb,
lib/otto/security/authentication.rb,
lib/otto/security/csrf_validation.rb,
lib/otto/caddy_tls/localhost_guard.rb,
lib/otto/response_handlers/default.rb,
lib/otto/response_handlers/factory.rb,
lib/otto/core/middleware_management.rb,
lib/otto/privacy/user_agent_privacy.rb,
lib/otto/response_handlers/redirect.rb,
lib/otto/route_handlers/logic_class.rb,
lib/otto/security/constant_resolver.rb,
lib/otto/privacy/anonymizer_resolver.rb,
lib/otto/route_handlers/class_method.rb,
lib/otto/security/csp/request_extras.rb,
lib/otto/privacy/redacted_fingerprint.rb,
lib/otto/security/authorization_error.rb,
lib/otto/security/csp/emit_middleware.rb,
lib/otto/route_handlers/instance_method.rb,
lib/otto/security/csp/report_middleware.rb,
lib/otto/security/csrf_enforcement_wrapper.rb,
lib/otto/security/middleware/csrf_middleware.rb,
lib/otto/security/authentication/auth_failure.rb,
lib/otto/security/authentication/auth_strategy.rb,
lib/otto/security/authentication/strategy_result.rb,
lib/otto/security/middleware/ip_privacy_middleware.rb,
lib/otto/security/middleware/rate_limit_middleware.rb,
lib/otto/security/middleware/validation_middleware.rb,
lib/otto/security/authentication/route_auth_wrapper.rb,
lib/otto/security/authentication/authorization_failure.rb,
lib/otto/security/authentication/strategies/role_strategy.rb,
lib/otto/security/authentication/strategies/noauth_strategy.rb,
lib/otto/security/authentication/strategies/api_key_strategy.rb,
lib/otto/security/authentication/strategies/session_strategy.rb,
lib/otto/security/authentication/strategies/permission_strategy.rb,
lib/otto/security/authentication/route_auth_wrapper/response_builder.rb,
lib/otto/security/authentication/route_auth_wrapper/strategy_resolver.rb,
lib/otto/security/authentication/route_auth_wrapper/role_authorization.rb

Overview

lib/otto/security/authentication/authorization_failure.rb

frozen_string_literal: true

Defined Under Namespace

Modules: BaseHelpers, CaddyTLS, ClassMethods, Core, DesignSystem, EnvKeys, Locale, LoggingHelpers, MCP, OptionalDependency, Privacy, ResponseHandlers, RouteHandlers, Security, Static, Utils Classes: BadRequestError, ForbiddenError, HTTPError, NotFoundError, OptionalDependencyError, PayloadTooLargeError, Request, Response, Route, RouteDefinition, RouteDefinitionError, UnauthorizedError

Constant Summary collapse

LIB_HOME =
__dir__
POSITIONAL_PARAMETER_TYPES =

Parameter types (from Proc#parameters / Method#parameters) that consume one positional argument each. See #fallback_call_args.

%i[req opt].freeze
StrategyResult =

Top-level backward compatibility aliases

Security::Authentication::StrategyResult
AuthFailure =
Security::Authentication::AuthFailure
AuthorizationFailure =
Security::Authentication::AuthorizationFailure

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from ClassMethods

default, env?, load, path, unfreeze_for_testing

Constructor Details

#initialize(path = nil, opts = {}) ⇒ Otto

Returns a new instance of Otto.



137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/otto.rb', line 137

def initialize(path = nil, opts = {})
  constructed = false
  initialize_core_state
  initialize_options(path, opts)
  initialize_configurations(opts)

  Otto.logger.debug "new Otto: #{opts}" if Otto.debug
  load(path) unless path.nil?
  super()

  # Auto-register all Otto framework error classes
  # This allows Logic classes and framework code to raise appropriate errors
  # without requiring manual registration in implementing projects
  register_framework_errors

  # Build the middleware app once after all initialization is complete
  build_app!

  # Configuration freezing is deferred until first request to support
  # multi-step initialization (e.g., multi-app architectures).
  # This allows adding auth strategies, middleware, etc. after Otto.new
  # but before processing requests.
  @freeze_mutex = Mutex.new
  @configuration_frozen = false
  constructed = true
ensure
  # A config that committed to a forwarding family during configure_security
  # is held process-wide; withdraw it if construction failed afterwards
  # (e.g. a bad routes path), or the dead app would keep vetoing other
  # families for the life of the process.
  Otto::Security::Config.release_rack_forwarding_family!(@security_config) if @security_config && !constructed
end

Class Attribute Details

.debugObject

rubocop:disable ThreadSafety/ClassAndModuleAttributes



325
326
327
# File 'lib/otto.rb', line 325

def debug
  @debug
end

.loggerObject

Otto's logger. Never nil: falls back to a default $stdout logger so call sites can log unconditionally without defensive &. guards. Assign your own logger (or a null logger to silence) via Otto.logger = ....



331
332
333
# File 'lib/otto.rb', line 331

def logger
  @logger ||= Logger.new($stdout, Logger::INFO)
end

Instance Attribute Details

#auth_configObject (readonly)

Returns the value of attribute auth_config.



84
85
86
# File 'lib/otto.rb', line 84

def auth_config
  @auth_config
end

#caddy_tls_serverObject (readonly)

Returns the value of attribute caddy_tls_server.



84
85
86
# File 'lib/otto.rb', line 84

def caddy_tls_server
  @caddy_tls_server
end

#error_handlersObject (readonly)

Returns the value of attribute error_handlers.



84
85
86
# File 'lib/otto.rb', line 84

def error_handlers
  @error_handlers
end

#locale_configObject (readonly)

Returns the value of attribute locale_config.



84
85
86
# File 'lib/otto.rb', line 84

def locale_config
  @locale_config
end

#mcp_serverObject (readonly)

Returns the value of attribute mcp_server.



84
85
86
# File 'lib/otto.rb', line 84

def mcp_server
  @mcp_server
end

#middlewareObject (readonly)

Returns the value of attribute middleware.



84
85
86
# File 'lib/otto.rb', line 84

def middleware
  @middleware
end

#not_foundObject

Returns the value of attribute not_found.



84
85
86
# File 'lib/otto.rb', line 84

def not_found
  @not_found
end

#optionObject (readonly) Also known as: options, config

Returns the value of attribute option.



84
85
86
# File 'lib/otto.rb', line 84

def option
  @option
end

#request_classObject (readonly)

Returns the value of attribute request_class.



84
85
86
# File 'lib/otto.rb', line 84

def request_class
  @request_class
end

#response_classObject (readonly)

Returns the value of attribute response_class.



84
85
86
# File 'lib/otto.rb', line 84

def response_class
  @response_class
end

#route_definitionsObject (readonly)

Returns the value of attribute route_definitions.



84
85
86
# File 'lib/otto.rb', line 84

def route_definitions
  @route_definitions
end

#route_handler_factoryObject (readonly)

Returns the value of attribute route_handler_factory.



84
85
86
# File 'lib/otto.rb', line 84

def route_handler_factory
  @route_handler_factory
end

#routesObject (readonly)

Returns the value of attribute routes.



84
85
86
# File 'lib/otto.rb', line 84

def routes
  @routes
end

#routes_by_definitionObject (readonly)

Returns the value of attribute routes_by_definition.



84
85
86
# File 'lib/otto.rb', line 84

def routes_by_definition
  @routes_by_definition
end

#routes_literalObject (readonly)

Returns the value of attribute routes_literal.



84
85
86
# File 'lib/otto.rb', line 84

def routes_literal
  @routes_literal
end

#securityObject (readonly)

Returns the value of attribute security.



84
85
86
# File 'lib/otto.rb', line 84

def security
  @security
end

#security_configObject (readonly)

Returns the value of attribute security_config.



84
85
86
# File 'lib/otto.rb', line 84

def security_config
  @security_config
end

#server_errorObject

Returns the value of attribute server_error.



84
85
86
# File 'lib/otto.rb', line 84

def server_error
  @server_error
end

#static_routeObject (readonly)

Returns the value of attribute static_route.



84
85
86
# File 'lib/otto.rb', line 84

def static_route
  @static_route
end

Class Method Details

.structured_log(level, message, data = {}) ⇒ Object

Helper method for structured logging that works with both standard Logger and structured loggers



336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
# File 'lib/otto.rb', line 336

def structured_log(level, message, data = {})
  return unless logger

  # Skip debug logging when Otto.debug is false
  return if level == :debug && !debug

  # Sanitize backtrace if present
  if data.is_a?(Hash) && data[:backtrace].is_a?(Array)
    data = data.dup
    data[:backtrace] = Otto::LoggingHelpers.sanitize_backtrace(data[:backtrace])
  end

  # Try structured logging first (SemanticLogger, etc.)
  if logger.respond_to?(level) && logger.method(level).arity > 1
    logger.send(level, message, data)
  else
    # Fallback to standard logger with formatted string
    formatted_data = data.empty? ? '' : " -- #{data.inspect}"
    logger.send(level, "[Otto] #{message}#{formatted_data}")
  end
end

Instance Method Details

#call(env) ⇒ Object

Main Rack application interface



176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
# File 'lib/otto.rb', line 176

def call(env)
  # Freeze configuration on first request (thread-safe).
  # Skipped under RSpec so specs can mutate configuration after construction.
  # Because of this skip, behavior that depends on a genuinely frozen config
  # (e.g. CSP violation dispatch through a frozen Config) is NOT exercised by
  # the normal request path in tests — cover it with specs that freeze
  # explicitly (see spec/otto/security/csp_reporting_frozen_spec.rb and
  # spec/otto/configuration_freezing_spec.rb).
  unless defined?(RSpec) || @configuration_frozen
    Otto.logger.debug '[Otto] Lazy freezing check: configuration not yet frozen' if Otto.debug

    @freeze_mutex.synchronize do
      unless @configuration_frozen
        Otto.logger.info '[Otto] Freezing configuration on first request (lazy freeze)'
        freeze_configuration!
        @configuration_frozen = true
        Otto.logger.debug '[Otto] Configuration frozen successfully' if Otto.debug
      end
    end
  end

  # Track request timing for lifecycle hooks
  start_time = Otto::Utils.now_in_μs
  request = @request_class.new(env)
  response_raw = nil

  begin
    # Use pre-built middleware app (built once at initialization)
    response_raw = @app.call(env)
  rescue StandardError => e
    response_raw = handle_error(e, env)
  ensure
    # Execute request completion hooks if any are registered
    unless @request_complete_callbacks.empty?
      begin
        duration = Otto::Utils.now_in_μs - start_time
        # Wrap response tuple in Otto::Response for developer-friendly API
        # Otto's hook API should provide nice abstractions like Otto::Request/Response
        response = @response_class.new(response_raw[2], response_raw[0], response_raw[1])
        @request_complete_callbacks.each do |callback|
          callback.call(request, response, duration)
        end
      rescue StandardError => e
        Otto.logger.error "[Otto] Request completion hook error: #{e.message}"
        Otto.logger.debug "[Otto] Hook error backtrace: #{e.backtrace.join("\n")}" if Otto.debug
      end
    end
  end

  response_raw
end