Module: Rodauth::SecretGuard
- Defined in:
- lib/rodauth/secret_guard.rb
Overview
Shared, secret-kind-parameterized logic behind the hmac_secret_guard and
jwt_secret_guard features.
The two guard features are nearly identical; the only thing that differs is
the "kind" of secret they manage (+:hmac+ or :jwt) and the names of the
configuration methods that carry that kind as a prefix
(+hmac_secret_env_key+ vs jwt_secret_env_key, and so on).
Keeping this logic in one place — and taking kind as an explicit argument
rather than baking it into method names — is what lets both features be
enabled at the same time. Each feature's post_configure calls into these
helpers with its own kind, so both secrets are validated at boot. When the
per-feature methods shared a name (the previous design), enabling both
guards meant one definition shadowed the other and only a single secret was
ever validated.
These are plain module functions that take the Rodauth instance explicitly (rather than being mixed in) so there is no method-name surface to collide in the first place.
Class Method Summary collapse
-
.blank?(value) ⇒ Boolean
True when the value is nil, empty, or whitespace-only.
-
.define_secret(rodauth, kind, value) ⇒ void
Redefine
<kind>_secreton the Rodauth subclass to returnvalue. -
.enforce_minimum_length!(rodauth, kind, value) ⇒ void
Enforce
minimum_secret_lengthwhen it is configured (> 0). -
.load_from_env!(rodauth, kind) ⇒ void
Auto-populate
<kind>_secretfrom its environment variable when it has not been configured explicitly. -
.production?(rodauth) ⇒ Boolean
Resolve the configured production check into a boolean.
-
.validate!(rodauth, kind) ⇒ void
Validate that
<kind>_secretis usable. -
.warn(rodauth, message) ⇒ void
Emit a development warning via the Rodauth logger when present, otherwise to stderr.
Class Method Details
.blank?(value) ⇒ Boolean
Returns true when the value is nil, empty, or whitespace-only.
111 112 113 |
# File 'lib/rodauth/secret_guard.rb', line 111 def blank?(value) value.nil? || value.to_s.strip.empty? end |
.define_secret(rodauth, kind, value) ⇒ void
This method returns an undefined value.
Redefine <kind>_secret on the Rodauth subclass to return value.
This mirrors how Rodauth features memoize resolved config: the auth class is per-configuration and this runs once, single-threaded, at boot.
121 122 123 |
# File 'lib/rodauth/secret_guard.rb', line 121 def define_secret(rodauth, kind, value) rodauth.class.send(:define_method, :"#{kind}_secret") { value } end |
.enforce_minimum_length!(rodauth, kind, value) ⇒ void
This method returns an undefined value.
Enforce minimum_secret_length when it is configured (> 0).
Only applied in production so development fallbacks and short test secrets are unaffected. Disabled by default.
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 |
# File 'lib/rodauth/secret_guard.rb', line 94 def enforce_minimum_length!(rodauth, kind, value) minimum = rodauth.send(:minimum_secret_length).to_i return if minimum <= 0 return unless rodauth.production? return if value.to_s.strip.length >= minimum # Name the secret generically and cite both configuration avenues: the # value may have come from the env var OR from the #{kind}_secret DSL # method, so blaming the env key alone would mislead when a short secret # was configured directly. key = rodauth.send(:"#{kind}_secret_env_key") raise Rodauth::ConfigurationError, "#{kind.to_s.upcase} secret must be at least #{minimum} characters in production " \ "(set via #{key} or #{kind}_secret)" end |
.load_from_env!(rodauth, kind) ⇒ void
This method returns an undefined value.
Auto-populate <kind>_secret from its environment variable when it has
not been configured explicitly.
The variable is read with ENV.delete so the raw secret does not linger
in the process environment after boot. A blank (nil/empty/whitespace-only)
value is treated as absent and leaves the secret unset for validate! to
handle.
41 42 43 44 45 46 47 48 49 |
# File 'lib/rodauth/secret_guard.rb', line 41 def load_from_env!(rodauth, kind) return unless blank?(rodauth.send(:"#{kind}_secret")) raw = ENV.delete(rodauth.send(:"#{kind}_secret_env_key")) value = raw&.strip return if value.nil? || value.empty? define_secret(rodauth, kind, value) end |
.production?(rodauth) ⇒ Boolean
Resolve the configured production check into a boolean.
A Proc is evaluated in the Rodauth instance context (so it can read config
methods); anything else is coerced with !!.
83 84 85 86 |
# File 'lib/rodauth/secret_guard.rb', line 83 def production?(rodauth) check = rodauth.send(:production_env_check) check.is_a?(Proc) ? rodauth.instance_exec(&check) : !!check end |
.validate!(rodauth, kind) ⇒ void
This method returns an undefined value.
Validate that <kind>_secret is usable.
In production a missing (nil/empty/whitespace-only) or too-short secret
raises Rodauth::ConfigurationError — the guard fails closed. Outside
production a missing secret logs a warning and falls back to the
(ephemeral, per-process) development fallback.
62 63 64 65 66 67 68 69 70 71 72 73 74 |
# File 'lib/rodauth/secret_guard.rb', line 62 def validate!(rodauth, kind) current = rodauth.send(:"#{kind}_secret") unless blank?(current) enforce_minimum_length!(rodauth, kind, current) return end raise Rodauth::ConfigurationError, rodauth.send(:"#{kind}_secret_missing_error") if rodauth.production? warn(rodauth, rodauth.send(:"#{kind}_secret_dev_warning")) define_secret(rodauth, kind, rodauth.send(:"development_#{kind}_secret_fallback")) end |
.warn(rodauth, message) ⇒ void
This method returns an undefined value.
Emit a development warning via the Rodauth logger when present, otherwise to stderr.
129 130 131 132 133 134 135 |
# File 'lib/rodauth/secret_guard.rb', line 129 def warn(rodauth, ) if rodauth.respond_to?(:logger) && rodauth.logger rodauth.logger.warn() else Kernel.warn() end end |