rodauth-tools 0.3.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 +7 -0
- data/.gitignore +93 -0
- data/.gitlint +9 -0
- data/.markdownlint-cli2.jsonc +26 -0
- data/.pre-commit-config.yaml +46 -0
- data/.rubocop.yml +18 -0
- data/.rubocop_todo.yml +243 -0
- data/CHANGELOG.md +81 -0
- data/CLAUDE.md +262 -0
- data/CODE_OF_CONDUCT.md +132 -0
- data/CONTRIBUTING.md +111 -0
- data/Gemfile +35 -0
- data/Gemfile.lock +356 -0
- data/LICENSE.txt +21 -0
- data/README.md +339 -0
- data/Rakefile +8 -0
- data/lib/rodauth/features/external_identity.rb +946 -0
- data/lib/rodauth/features/hmac_secret_guard.rb +119 -0
- data/lib/rodauth/features/jwt_secret_guard.rb +120 -0
- data/lib/rodauth/features/table_guard.rb +937 -0
- data/lib/rodauth/sequel_generator.rb +531 -0
- data/lib/rodauth/table_inspector.rb +124 -0
- data/lib/rodauth/template_inspector.rb +134 -0
- data/lib/rodauth/tools/console_helpers.rb +158 -0
- data/lib/rodauth/tools/migration/sequel/account_expiration.erb +9 -0
- data/lib/rodauth/tools/migration/sequel/active_sessions.erb +10 -0
- data/lib/rodauth/tools/migration/sequel/audit_logging.erb +12 -0
- data/lib/rodauth/tools/migration/sequel/base.erb +41 -0
- data/lib/rodauth/tools/migration/sequel/disallow_password_reuse.erb +8 -0
- data/lib/rodauth/tools/migration/sequel/email_auth.erb +17 -0
- data/lib/rodauth/tools/migration/sequel/jwt_refresh.erb +18 -0
- data/lib/rodauth/tools/migration/sequel/lockout.erb +21 -0
- data/lib/rodauth/tools/migration/sequel/otp.erb +9 -0
- data/lib/rodauth/tools/migration/sequel/otp_unlock.erb +8 -0
- data/lib/rodauth/tools/migration/sequel/password_expiration.erb +7 -0
- data/lib/rodauth/tools/migration/sequel/recovery_codes.erb +8 -0
- data/lib/rodauth/tools/migration/sequel/remember.erb +16 -0
- data/lib/rodauth/tools/migration/sequel/reset_password.erb +17 -0
- data/lib/rodauth/tools/migration/sequel/single_session.erb +7 -0
- data/lib/rodauth/tools/migration/sequel/sms_codes.erb +10 -0
- data/lib/rodauth/tools/migration/sequel/verify_account.erb +9 -0
- data/lib/rodauth/tools/migration/sequel/verify_login_change.erb +17 -0
- data/lib/rodauth/tools/migration/sequel/webauthn.erb +15 -0
- data/lib/rodauth/tools/migration.rb +188 -0
- data/lib/rodauth/tools/version.rb +9 -0
- data/lib/rodauth/tools.rb +29 -0
- data/package-lock.json +500 -0
- data/package.json +11 -0
- data/rodauth-tools.gemspec +40 -0
- metadata +136 -0
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
# lib/rodauth/features/hmac_secret_guard.rb
|
|
2
|
+
#
|
|
3
|
+
# frozen_string_literal: true
|
|
4
|
+
|
|
5
|
+
module Rodauth
|
|
6
|
+
# Automatically sets hmac_secret based on HMAC_SECRET and validates it is properly
|
|
7
|
+
# configured before the application starts. This helps prevent deployment
|
|
8
|
+
# errors where secret environment variables might not be set correctly,
|
|
9
|
+
# particularly in production environments.
|
|
10
|
+
#
|
|
11
|
+
# By default, this feature checks during +post_configure+ that +hmac_secret+
|
|
12
|
+
# is set to a non-nil, non-empty value. In production mode, it raises a
|
|
13
|
+
# ConfigurationError if the secret is missing. In development mode, it logs
|
|
14
|
+
# a warning and uses a fallback development secret.
|
|
15
|
+
#
|
|
16
|
+
# @example Basic Configuration
|
|
17
|
+
# plugin :rodauth do
|
|
18
|
+
# enable :hmac_secret_guard
|
|
19
|
+
# end
|
|
20
|
+
#
|
|
21
|
+
# @example Customizing Production Detection
|
|
22
|
+
# plugin :rodauth do
|
|
23
|
+
# enable :hmac_secret_guard
|
|
24
|
+
# production_env_check proc { ENV['RACK_ENV'] == 'production' }
|
|
25
|
+
# # Or use a boolean:
|
|
26
|
+
# # production_env_check true
|
|
27
|
+
# end
|
|
28
|
+
#
|
|
29
|
+
# @example Customizing Error Messages
|
|
30
|
+
# plugin :rodauth do
|
|
31
|
+
# enable :hmac_secret_guard
|
|
32
|
+
# hmac_secret_missing_error 'HMAC secret must be configured in production!'
|
|
33
|
+
# hmac_secret_dev_warning 'WARNING: Using insecure development HMAC secret'
|
|
34
|
+
# end
|
|
35
|
+
#
|
|
36
|
+
# @example Customizing Development Fallback
|
|
37
|
+
# plugin :rodauth do
|
|
38
|
+
# enable :hmac_secret_guard
|
|
39
|
+
# development_hmac_secret_fallback 'my-custom-dev-secret'
|
|
40
|
+
# end
|
|
41
|
+
#
|
|
42
|
+
# @example Disabling Validation
|
|
43
|
+
# plugin :rodauth do
|
|
44
|
+
# enable :hmac_secret_guard
|
|
45
|
+
# validate_secrets_on_configure? false
|
|
46
|
+
# end
|
|
47
|
+
#
|
|
48
|
+
Feature.define(:hmac_secret_guard, :HmacSecretGuard) do
|
|
49
|
+
auth_value_method :hmac_secret_env_key, 'HMAC_SECRET'
|
|
50
|
+
auth_value_method :production_env_check, proc { ENV.fetch('RACK_ENV', 'production') == 'production' }
|
|
51
|
+
auth_value_method :validate_secrets_on_configure?, true
|
|
52
|
+
auth_value_method :development_hmac_secret_fallback,
|
|
53
|
+
'dev-only-insecure-example-hmac-secret-needs-to-be-changed-in-prod'
|
|
54
|
+
|
|
55
|
+
translatable_method :hmac_secret_missing_error, 'HMAC_SECRET environment variable must be set in production'
|
|
56
|
+
translatable_method :hmac_secret_dev_warning, '[rodauth] WARNING: Using default HMAC secret for development only'
|
|
57
|
+
|
|
58
|
+
def post_configure
|
|
59
|
+
super
|
|
60
|
+
|
|
61
|
+
# Auto-set hmac_secret if not already set
|
|
62
|
+
if hmac_secret.nil? || (hmac_secret.respond_to?(:empty?) && hmac_secret.empty?)
|
|
63
|
+
env_value = ENV.delete(hmac_secret_env_key)
|
|
64
|
+
self.class.send(:define_method, :hmac_secret) { env_value } if env_value && !env_value.empty?
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
validate_secrets! if validate_secrets_on_configure?
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
auth_methods :validate_secrets!, :production?
|
|
71
|
+
|
|
72
|
+
# Check if we're running in production environment.
|
|
73
|
+
#
|
|
74
|
+
# @return [Boolean] true if running in production mode based on production_env_check
|
|
75
|
+
def production?
|
|
76
|
+
case v = production_env_check
|
|
77
|
+
when Proc
|
|
78
|
+
instance_exec(&v)
|
|
79
|
+
else
|
|
80
|
+
!!v
|
|
81
|
+
end
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Validate that HMAC secret is properly configured.
|
|
85
|
+
# Raises ConfigurationError in production if secret is missing.
|
|
86
|
+
# In development, logs a warning and sets a fallback secret.
|
|
87
|
+
#
|
|
88
|
+
# @raise [Rodauth::ConfigurationError] if hmac_secret is missing in production
|
|
89
|
+
# @return [void]
|
|
90
|
+
def validate_secrets!
|
|
91
|
+
# Get the current hmac_secret value (may be nil)
|
|
92
|
+
current_secret = hmac_secret
|
|
93
|
+
|
|
94
|
+
# Check if secret is missing or empty
|
|
95
|
+
return unless current_secret.nil? || (current_secret.respond_to?(:empty?) && current_secret.empty?)
|
|
96
|
+
raise Rodauth::ConfigurationError, hmac_secret_missing_error if production?
|
|
97
|
+
|
|
98
|
+
# In production, raise an error
|
|
99
|
+
|
|
100
|
+
# In development, warn and set a fallback
|
|
101
|
+
warn_dev_secret
|
|
102
|
+
self.class.send(:define_method, :hmac_secret) { development_hmac_secret_fallback }
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
private
|
|
106
|
+
|
|
107
|
+
# Warn about using development secret.
|
|
108
|
+
# Logs to logger if available, otherwise to stderr.
|
|
109
|
+
#
|
|
110
|
+
# @return [void]
|
|
111
|
+
def warn_dev_secret
|
|
112
|
+
if respond_to?(:logger) && logger
|
|
113
|
+
logger.warn(hmac_secret_dev_warning)
|
|
114
|
+
else
|
|
115
|
+
warn(hmac_secret_dev_warning)
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# lib/rodauth/features/jwt_secret_guard.rb
|
|
2
|
+
#
|
|
3
|
+
# frozen_string_literal: true
|
|
4
|
+
|
|
5
|
+
module Rodauth
|
|
6
|
+
# Automatically sets jwt_secret based on JWT_SECRET and validates it is properly
|
|
7
|
+
# configured before the application starts. This helps prevent deployment
|
|
8
|
+
# errors where secret environment variables might not be set correctly,
|
|
9
|
+
# particularly in production environments.
|
|
10
|
+
#
|
|
11
|
+
# By default, this feature checks during +post_configure+ that +jwt_secret+
|
|
12
|
+
# is set to a non-nil, non-empty value. In production mode, it raises a
|
|
13
|
+
# ConfigurationError if the secret is missing. In development mode, it logs
|
|
14
|
+
# a warning and uses a fallback development secret.
|
|
15
|
+
#
|
|
16
|
+
# @example Basic Configuration
|
|
17
|
+
# plugin :rodauth do
|
|
18
|
+
# enable :jwt_secret_guard
|
|
19
|
+
# end
|
|
20
|
+
#
|
|
21
|
+
# @example Customizing Production Detection
|
|
22
|
+
# plugin :rodauth do
|
|
23
|
+
# enable :jwt_secret_guard
|
|
24
|
+
# production_env_check proc { ENV['RACK_ENV'] == 'production' }
|
|
25
|
+
# # Or use a boolean:
|
|
26
|
+
# # production_env_check true
|
|
27
|
+
# end
|
|
28
|
+
#
|
|
29
|
+
# @example Customizing Error Messages
|
|
30
|
+
# plugin :rodauth do
|
|
31
|
+
# enable :jwt_secret_guard
|
|
32
|
+
# jwt_secret_missing_error 'JWT secret must be configured in production!'
|
|
33
|
+
# jwt_secret_dev_warning 'WARNING: Using insecure development JWT secret'
|
|
34
|
+
# end
|
|
35
|
+
#
|
|
36
|
+
# @example Customizing Development Fallback
|
|
37
|
+
# plugin :rodauth do
|
|
38
|
+
# enable :jwt_secret_guard
|
|
39
|
+
# development_jwt_secret_fallback 'my-custom-dev-secret'
|
|
40
|
+
# end
|
|
41
|
+
#
|
|
42
|
+
# @example Disabling Validation
|
|
43
|
+
# plugin :rodauth do
|
|
44
|
+
# enable :jwt_secret_guard
|
|
45
|
+
# validate_secrets_on_configure? false
|
|
46
|
+
# end
|
|
47
|
+
#
|
|
48
|
+
Feature.define(:jwt_secret_guard, :JwtSecretGuard) do
|
|
49
|
+
auth_value_method :jwt_secret_env_key, 'JWT_SECRET'
|
|
50
|
+
auth_value_method :production_env_check, proc { ENV.fetch('RACK_ENV', 'production') == 'production' }
|
|
51
|
+
auth_value_method :validate_secrets_on_configure?, true
|
|
52
|
+
auth_value_method :development_jwt_secret_fallback,
|
|
53
|
+
'dev-only-insecure-example-jwt-secret-needs-to-be-changed-in-prod'
|
|
54
|
+
|
|
55
|
+
# Make jwt_secret configurable (if not already provided by jwt feature)
|
|
56
|
+
auth_value_method :jwt_secret, nil
|
|
57
|
+
|
|
58
|
+
translatable_method :jwt_secret_missing_error, 'JWT_SECRET environment variable must be set in production'
|
|
59
|
+
translatable_method :jwt_secret_dev_warning, '[rodauth] WARNING: Using default JWT secret for development only'
|
|
60
|
+
|
|
61
|
+
def post_configure
|
|
62
|
+
super
|
|
63
|
+
|
|
64
|
+
# Auto-set jwt_secret if not already set
|
|
65
|
+
if jwt_secret.nil? || (jwt_secret.respond_to?(:empty?) && jwt_secret.empty?)
|
|
66
|
+
env_value = ENV.delete(jwt_secret_env_key)
|
|
67
|
+
self.class.send(:define_method, :jwt_secret) { env_value } if env_value && !env_value.empty?
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
validate_secrets! if validate_secrets_on_configure?
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
auth_methods :validate_secrets!, :production?
|
|
74
|
+
|
|
75
|
+
# Check if we're running in production environment.
|
|
76
|
+
#
|
|
77
|
+
# @return [Boolean] true if running in production mode based on production_env_check
|
|
78
|
+
def production?
|
|
79
|
+
case v = production_env_check
|
|
80
|
+
when Proc
|
|
81
|
+
instance_exec(&v)
|
|
82
|
+
else
|
|
83
|
+
!!v
|
|
84
|
+
end
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Validate that JWT secret is properly configured.
|
|
88
|
+
# Raises ConfigurationError in production if secret is missing.
|
|
89
|
+
# In development, logs a warning and sets a fallback secret.
|
|
90
|
+
#
|
|
91
|
+
# @raise [Rodauth::ConfigurationError] if jwt_secret is missing in production
|
|
92
|
+
# @return [void]
|
|
93
|
+
def validate_secrets!
|
|
94
|
+
# Get the current jwt_secret value (may be nil)
|
|
95
|
+
current_secret = jwt_secret
|
|
96
|
+
|
|
97
|
+
# Return early if secret is present
|
|
98
|
+
return unless current_secret.nil? || (current_secret.respond_to?(:empty?) && current_secret.empty?)
|
|
99
|
+
raise Rodauth::ConfigurationError, jwt_secret_missing_error if production?
|
|
100
|
+
|
|
101
|
+
# In development, warn and set a fallback
|
|
102
|
+
warn_dev_secret
|
|
103
|
+
self.class.send(:define_method, :jwt_secret) { development_jwt_secret_fallback }
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
private
|
|
107
|
+
|
|
108
|
+
# Warn about using development secret.
|
|
109
|
+
# Logs to logger if available, otherwise to stderr.
|
|
110
|
+
#
|
|
111
|
+
# @return [void]
|
|
112
|
+
def warn_dev_secret
|
|
113
|
+
if respond_to?(:logger) && logger
|
|
114
|
+
logger.warn(jwt_secret_dev_warning)
|
|
115
|
+
else
|
|
116
|
+
warn(jwt_secret_dev_warning)
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|