Class: Otto::Security::Configurator
- Inherits:
-
Object
- Object
- Otto::Security::Configurator
- Defined in:
- lib/otto/security/configurator.rb
Overview
Consolidates all security configuration methods into a single configurator class. This provides a unified interface for configuring CSRF protection, input validation, rate limiting, trusted proxies, and authentication strategies.
Defined Under Namespace
Classes: ConfigureOptions
Constant Summary collapse
- CONFIGURE_DEFAULTS =
Options accepted by #configure, with their defaults. A new security knob is a new entry here, not another keyword parameter.
{ csrf_protection: false, request_validation: false, rate_limiting: false, trusted_proxies: [].freeze, trusted_proxy_depth: nil, trusted_proxy_header: nil, security_headers: {}.freeze, hsts: false, csp: false, frame_protection: false, authentication: false, }.freeze
Instance Attribute Summary collapse
-
#auth_config ⇒ Object
Returns the value of attribute auth_config.
-
#middleware_stack ⇒ Object
readonly
Returns the value of attribute middleware_stack.
-
#security_config ⇒ Object
readonly
Returns the value of attribute security_config.
Instance Method Summary collapse
-
#add_auth_strategy(name, strategy) ⇒ Object
Add a single authentication strategy.
-
#add_rate_limit_rule(name, options) ⇒ Object
Add a custom rate limiting rule.
-
#add_trusted_proxy(proxy) ⇒ Object
Add a trusted proxy server for accurate client IP detection.
-
#configure(**options) ⇒ Object
Unified security configuration method with sensible defaults.
-
#configure_auth_strategies(strategies, default_strategy: 'noauth') ⇒ Object
Configure authentication strategies for route-level access control.
-
#configure_rate_limiting(config) ⇒ Object
Configure rate limiting settings.
-
#csp_report_to_url=(url) ⇒ Object
Configure the absolute URL for the modern Reporting API endpoint (
report-todirective +Reporting-Endpointsheader) without injecting middleware. -
#csp_report_uri=(uri) ⇒ Object
Configure the CSP violation report path without injecting middleware.
-
#enable_csp!(policy = "default-src 'self'") ⇒ Object
Enable Content Security Policy (CSP) header to prevent XSS attacks.
-
#enable_csp_emission!(eager: false, development_mode: nil) ⇒ Object
Mount Otto::Security::CSP::EmitMiddleware (passive backstop that emits a nonce CSP for responses lacking one, never clobbering).
-
#enable_csp_reporting!(report_uri, endpoint_url: nil) {|report| ... } ⇒ Object
Enable turnkey CSP violation reporting: set the report URI (appends a
report-uridirective to emitted policies), register the callback, and inject Otto::Security::CSP::ReportMiddleware pinned OUTERMOST so it intercepts report POSTs ahead of CSRF regardless of enable order. -
#enable_csp_with_nonce!(debug: false) ⇒ Object
Enable Content Security Policy (CSP) with nonce support for dynamic header generation.
-
#enable_csrf_protection! ⇒ Object
Enable CSRF protection for POST, PUT, DELETE, and PATCH requests.
-
#enable_frame_protection!(option = 'SAMEORIGIN') ⇒ Object
Enable X-Frame-Options header to prevent clickjacking attacks.
-
#enable_hsts!(max_age: 31_536_000, include_subdomains: true) ⇒ Object
Enable HTTP Strict Transport Security (HSTS) header.
-
#enable_rate_limiting!(options = {}) ⇒ Object
Enable rate limiting to protect against abuse and DDoS attacks.
-
#enable_request_validation! ⇒ Object
Enable request validation including input sanitization, size limits, and protection against XSS and SQL injection attacks.
-
#initialize(security_config, middleware_stack, auth_config = nil) ⇒ Configurator
constructor
A new instance of Configurator.
-
#referrer_policy=(policy) ⇒ Object
Set the Referrer-Policy value added to Otto responses.
-
#security_headers=(headers) ⇒ Object
Set custom security headers that will be added to all responses.
-
#trust_no_proxies! ⇒ void
Assert that NO proxy is trusted (equivalent to
trusted_proxies: :none). -
#trusted_proxy_depth=(depth) ⇒ Object
Set count-based trusted-proxy depth ("trust the last N hops") for non-enumerable proxy tiers (Fly, cloud load balancers, dynamic reverse proxies).
-
#trusted_proxy_header=(header) ⇒ Object
Select which forwarded header depth mode counts hops from: 'X-Forwarded-For' (default), 'Forwarded' (RFC 7239), or 'Both'.
Constructor Details
#initialize(security_config, middleware_stack, auth_config = nil) ⇒ Configurator
Returns a new instance of Configurator.
19 20 21 22 23 24 |
# File 'lib/otto/security/configurator.rb', line 19 def initialize(security_config, middleware_stack, auth_config = nil) @security_config = security_config @middleware_stack = middleware_stack # Use provided auth_config or initialize a new one @auth_config = auth_config || { auth_strategies: {}, default_auth_strategy: 'noauth' } end |
Instance Attribute Details
#auth_config ⇒ Object
Returns the value of attribute auth_config.
17 18 19 |
# File 'lib/otto/security/configurator.rb', line 17 def auth_config @auth_config end |
#middleware_stack ⇒ Object (readonly)
Returns the value of attribute middleware_stack.
16 17 18 |
# File 'lib/otto/security/configurator.rb', line 16 def middleware_stack @middleware_stack end |
#security_config ⇒ Object (readonly)
Returns the value of attribute security_config.
16 17 18 |
# File 'lib/otto/security/configurator.rb', line 16 def security_config @security_config end |
Instance Method Details
#add_auth_strategy(name, strategy) ⇒ Object
Add a single authentication strategy
Part of the Security::Configurator facade for consolidated configuration. This delegates to the same storage as Otto#add_auth_strategy, allowing authentication to be configured alongside other security features.
Prefer using Otto#add_auth_strategy directly for simpler cases, or use this when configuring multiple security features together via the security facade.
324 325 326 327 328 329 330 331 |
# File 'lib/otto/security/configurator.rb', line 324 def add_auth_strategy(name, strategy) # Strict mode: Detect strategy name collisions if @auth_config[:auth_strategies].key?(name) raise ArgumentError, "Authentication strategy '#{name}' is already registered" end @auth_config[:auth_strategies][name] = strategy end |
#add_rate_limit_rule(name, options) ⇒ Object
Add a custom rate limiting rule.
159 160 161 |
# File 'lib/otto/security/configurator.rb', line 159 def add_rate_limit_rule(name, ) @security_config.rate_limiting_config[:custom_rules][name.to_s] = end |
#add_trusted_proxy(proxy) ⇒ Object
Add a trusted proxy server for accurate client IP detection. Only requests from trusted proxies will have their forwarded headers honored.
167 168 169 |
# File 'lib/otto/security/configurator.rb', line 167 def add_trusted_proxy(proxy) @security_config.add_trusted_proxy(proxy) end |
#configure(**options) ⇒ Object
Unified security configuration method with sensible defaults
Provides a comprehensive, one-stop configuration method for Otto's security features. This method allows configuring multiple security aspects in a single call, with flexible options. Every option is optional; see CONFIGURE_DEFAULTS.
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
# File 'lib/otto/security/configurator.rb', line 92 def configure(**) opts = () enable_csrf_protection! if opts.csrf_protection enable_request_validation! if opts.request_validation enable_rate_limiting!(opts.rate_limiting.is_a?(Hash) ? opts.rate_limiting : {}) if opts.rate_limiting if Otto::Security::Config.trust_no_proxies_option?(opts.trusted_proxies) trust_no_proxies! else # Pass the list whole so add_trusted_proxy validates every entry # before registering any; a mixed list like ['10.0.0.0/8', 'none'] # must not leave the first half installed. add_trusted_proxy(Array(opts.trusted_proxies)) unless Array(opts.trusted_proxies).empty? end self.trusted_proxy_depth = opts.trusted_proxy_depth unless opts.trusted_proxy_depth.nil? self.trusted_proxy_header = opts.trusted_proxy_header unless opts.trusted_proxy_header.nil? # Proxy trust configured here (after Otto.new) commits the app to its # forwarding family now rather than at freeze. @security_config.commit_rack_forwarding_family! self.security_headers = opts.security_headers unless opts.security_headers.empty? enable_hsts! if opts.hsts enable_csp! if opts.csp enable_frame_protection! if opts.frame_protection end |
#configure_auth_strategies(strategies, default_strategy: 'noauth') ⇒ Object
Configure authentication strategies for route-level access control.
337 338 339 340 341 |
# File 'lib/otto/security/configurator.rb', line 337 def configure_auth_strategies(strategies, default_strategy: 'noauth') # Merge new strategies with existing ones, preserving shared state @auth_config[:auth_strategies].merge!(strategies) @auth_config[:default_auth_strategy] = default_strategy end |
#configure_rate_limiting(config) ⇒ Object
Configure rate limiting settings.
349 350 351 |
# File 'lib/otto/security/configurator.rb', line 349 def configure_rate_limiting(config) @security_config.rate_limiting_config.merge!(config) end |
#csp_report_to_url=(url) ⇒ Object
Configure the absolute URL for the modern Reporting API endpoint
(report-to directive + Reporting-Endpoints header) without injecting
middleware. Prefer #enable_csp_reporting! with endpoint_url: for the
full turnkey setup.
306 307 308 |
# File 'lib/otto/security/configurator.rb', line 306 def csp_report_to_url=(url) @security_config.csp_report_to_url = url end |
#csp_report_uri=(uri) ⇒ Object
Configure the CSP violation report path without injecting middleware. Prefer #enable_csp_reporting! for the full turnkey setup.
296 297 298 |
# File 'lib/otto/security/configurator.rb', line 296 def csp_report_uri=(uri) @security_config.csp_report_uri = uri end |
#enable_csp!(policy = "default-src 'self'") ⇒ Object
Enable Content Security Policy (CSP) header to prevent XSS attacks. The default policy only allows resources from the same origin.
235 236 237 |
# File 'lib/otto/security/configurator.rb', line 235 def enable_csp!(policy = "default-src 'self'") @security_config.enable_csp!(policy) end |
#enable_csp_emission!(eager: false, development_mode: nil) ⇒ Object
Mount Otto::Security::CSP::EmitMiddleware (passive backstop that emits a nonce CSP for responses lacking one, never clobbering). Enable nonce-CSP (#enable_csp_with_nonce!) for it to emit anything; until then it is INERT (a transparent pass-through), not an error, and the two may be enabled in either order. Emit-if-consumed by default — see Otto::Security::Core#enable_csp_emission!.
264 265 266 267 268 |
# File 'lib/otto/security/configurator.rb', line 264 def enable_csp_emission!(eager: false, development_mode: nil) return if middleware_enabled?(Otto::Security::CSP::EmitMiddleware) @middleware_stack.add(Otto::Security::CSP::EmitMiddleware, eager: eager, development_mode: development_mode) end |
#enable_csp_reporting!(report_uri, endpoint_url: nil) {|report| ... } ⇒ Object
Enable turnkey CSP violation reporting: set the report URI (appends a
report-uri directive to emitted policies), register the callback, and
inject Otto::Security::CSP::ReportMiddleware pinned OUTERMOST so it
intercepts report POSTs ahead of CSRF regardless of enable order.
281 282 283 284 285 286 287 288 289 |
# File 'lib/otto/security/configurator.rb', line 281 def enable_csp_reporting!(report_uri, endpoint_url: nil, &block) @security_config.csp_report_uri = report_uri @security_config.csp_report_to_url = endpoint_url unless endpoint_url.nil? @security_config.on_csp_violation(&block) if block return if middleware_enabled?(Otto::Security::CSP::ReportMiddleware) @middleware_stack.add_with_position(Otto::Security::CSP::ReportMiddleware, position: :outermost) end |
#enable_csp_with_nonce!(debug: false) ⇒ Object
Enable Content Security Policy (CSP) with nonce support for dynamic header generation. This enables the res.send_csp_headers response helper method.
250 251 252 |
# File 'lib/otto/security/configurator.rb', line 250 def enable_csp_with_nonce!(debug: false) @security_config.enable_csp_with_nonce!(debug: debug) end |
#enable_csrf_protection! ⇒ Object
Enable CSRF protection for POST, PUT, DELETE, and PATCH requests. This will automatically add CSRF tokens to HTML forms and validate them on unsafe HTTP methods.
122 123 124 125 126 127 |
# File 'lib/otto/security/configurator.rb', line 122 def enable_csrf_protection! return if middleware_enabled?(Otto::Security::Middleware::CSRFMiddleware) @security_config.enable_csrf_protection! @middleware_stack.add(Otto::Security::Middleware::CSRFMiddleware) end |
#enable_frame_protection!(option = 'SAMEORIGIN') ⇒ Object
Enable X-Frame-Options header to prevent clickjacking attacks.
242 243 244 |
# File 'lib/otto/security/configurator.rb', line 242 def enable_frame_protection!(option = 'SAMEORIGIN') @security_config.enable_frame_protection!(option) end |
#enable_hsts!(max_age: 31_536_000, include_subdomains: true) ⇒ Object
Enable HTTP Strict Transport Security (HSTS) header. WARNING: This can make your domain inaccessible if HTTPS is not properly configured. Only enable this when you're certain HTTPS is working correctly.
227 228 229 |
# File 'lib/otto/security/configurator.rb', line 227 def enable_hsts!(max_age: 31_536_000, include_subdomains: true) @security_config.enable_hsts!(max_age: max_age, include_subdomains: include_subdomains) end |
#enable_rate_limiting!(options = {}) ⇒ Object
Enable rate limiting to protect against abuse and DDoS attacks. This will automatically add rate limiting rules based on client IP.
144 145 146 147 148 149 150 |
# File 'lib/otto/security/configurator.rb', line 144 def enable_rate_limiting!( = {}) return if middleware_enabled?(Otto::Security::Middleware::RateLimitMiddleware) Otto::Security::RateLimiting.ensure_available! configure_rate_limiting() @middleware_stack.add(Otto::Security::Middleware::RateLimitMiddleware) end |
#enable_request_validation! ⇒ Object
Enable request validation including input sanitization, size limits, and protection against XSS and SQL injection attacks.
131 132 133 134 135 136 |
# File 'lib/otto/security/configurator.rb', line 131 def enable_request_validation! return if middleware_enabled?(Otto::Security::Middleware::ValidationMiddleware) @security_config.input_validation = true @middleware_stack.add(Otto::Security::Middleware::ValidationMiddleware) end |
#referrer_policy=(policy) ⇒ Object
Set the Referrer-Policy value added to Otto responses.
217 218 219 |
# File 'lib/otto/security/configurator.rb', line 217 def referrer_policy=(policy) @security_config.referrer_policy = policy end |
#security_headers=(headers) ⇒ Object
Set custom security headers that will be added to all responses. These merge with the default security headers.
210 211 212 |
# File 'lib/otto/security/configurator.rb', line 210 def security_headers=(headers) @security_config.security_headers.merge!(headers) end |
#trust_no_proxies! ⇒ void
This method returns an undefined value.
Assert that NO proxy is trusted (equivalent to trusted_proxies: :none). Makes env false for every peer, so
forwarded client-IP and host/scheme/port carriers are ignored and
stripped. See Otto::Security::Config#trust_no_proxies!.
178 179 180 |
# File 'lib/otto/security/configurator.rb', line 178 def trust_no_proxies! @security_config.trust_no_proxies! end |
#trusted_proxy_depth=(depth) ⇒ Object
Set count-based trusted-proxy depth ("trust the last N hops") for non-enumerable proxy tiers (Fly, cloud load balancers, dynamic reverse proxies). Mutually exclusive with trusted_proxies; the conflict is validated when the configuration is frozen.
188 189 190 |
# File 'lib/otto/security/configurator.rb', line 188 def trusted_proxy_depth=(depth) @security_config.trusted_proxy_depth = depth end |
#trusted_proxy_header=(header) ⇒ Object
Select which forwarded header depth mode counts hops from:
'X-Forwarded-For' (default), 'Forwarded' (RFC 7239), or 'Both'. Otto
reads the value only when depth mode is active, but setting it always
pins Rack::Request.forwarded_priority (process-global) to that family
and claims the family for this process, even under
trusted_proxies: :none; see
Otto::Security::Config.apply_rack_forwarding_family!. Mirrors
OneTimeSecret's site.network.trusted_proxy.header.
202 203 204 |
# File 'lib/otto/security/configurator.rb', line 202 def trusted_proxy_header=(header) @security_config.trusted_proxy_header = header end |