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.
Files changed (50) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +93 -0
  3. data/.gitlint +9 -0
  4. data/.markdownlint-cli2.jsonc +26 -0
  5. data/.pre-commit-config.yaml +46 -0
  6. data/.rubocop.yml +18 -0
  7. data/.rubocop_todo.yml +243 -0
  8. data/CHANGELOG.md +81 -0
  9. data/CLAUDE.md +262 -0
  10. data/CODE_OF_CONDUCT.md +132 -0
  11. data/CONTRIBUTING.md +111 -0
  12. data/Gemfile +35 -0
  13. data/Gemfile.lock +356 -0
  14. data/LICENSE.txt +21 -0
  15. data/README.md +339 -0
  16. data/Rakefile +8 -0
  17. data/lib/rodauth/features/external_identity.rb +946 -0
  18. data/lib/rodauth/features/hmac_secret_guard.rb +119 -0
  19. data/lib/rodauth/features/jwt_secret_guard.rb +120 -0
  20. data/lib/rodauth/features/table_guard.rb +937 -0
  21. data/lib/rodauth/sequel_generator.rb +531 -0
  22. data/lib/rodauth/table_inspector.rb +124 -0
  23. data/lib/rodauth/template_inspector.rb +134 -0
  24. data/lib/rodauth/tools/console_helpers.rb +158 -0
  25. data/lib/rodauth/tools/migration/sequel/account_expiration.erb +9 -0
  26. data/lib/rodauth/tools/migration/sequel/active_sessions.erb +10 -0
  27. data/lib/rodauth/tools/migration/sequel/audit_logging.erb +12 -0
  28. data/lib/rodauth/tools/migration/sequel/base.erb +41 -0
  29. data/lib/rodauth/tools/migration/sequel/disallow_password_reuse.erb +8 -0
  30. data/lib/rodauth/tools/migration/sequel/email_auth.erb +17 -0
  31. data/lib/rodauth/tools/migration/sequel/jwt_refresh.erb +18 -0
  32. data/lib/rodauth/tools/migration/sequel/lockout.erb +21 -0
  33. data/lib/rodauth/tools/migration/sequel/otp.erb +9 -0
  34. data/lib/rodauth/tools/migration/sequel/otp_unlock.erb +8 -0
  35. data/lib/rodauth/tools/migration/sequel/password_expiration.erb +7 -0
  36. data/lib/rodauth/tools/migration/sequel/recovery_codes.erb +8 -0
  37. data/lib/rodauth/tools/migration/sequel/remember.erb +16 -0
  38. data/lib/rodauth/tools/migration/sequel/reset_password.erb +17 -0
  39. data/lib/rodauth/tools/migration/sequel/single_session.erb +7 -0
  40. data/lib/rodauth/tools/migration/sequel/sms_codes.erb +10 -0
  41. data/lib/rodauth/tools/migration/sequel/verify_account.erb +9 -0
  42. data/lib/rodauth/tools/migration/sequel/verify_login_change.erb +17 -0
  43. data/lib/rodauth/tools/migration/sequel/webauthn.erb +15 -0
  44. data/lib/rodauth/tools/migration.rb +188 -0
  45. data/lib/rodauth/tools/version.rb +9 -0
  46. data/lib/rodauth/tools.rb +29 -0
  47. data/package-lock.json +500 -0
  48. data/package.json +11 -0
  49. data/rodauth-tools.gemspec +40 -0
  50. 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