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,188 @@
1
+ # lib/rodauth/tools/migration.rb
2
+ #
3
+ # frozen_string_literal: true
4
+
5
+ require 'erb'
6
+ require 'dry/inflector'
7
+
8
+ module Rodauth
9
+ module Tools
10
+ # Sequel migration generator for Rodauth database tables.
11
+ #
12
+ # @deprecated This static migration generator is deprecated in favor of
13
+ # the dynamic table_guard feature with sequel generation modes.
14
+ # Use table_guard_sequel_mode instead for automatic migration generation.
15
+ #
16
+ # Generates migrations for Sequel ORM, supporting
17
+ # PostgreSQL, MySQL, and SQLite databases.
18
+ #
19
+ # @example Generate a migration (DEPRECATED)
20
+ # generator = Rodauth::Tools::Migration.new(
21
+ # features: [:base, :verify_account, :otp],
22
+ # prefix: "account",
23
+ # db_adapter: :postgresql
24
+ # )
25
+ #
26
+ # generator.generate # => migration content
27
+ #
28
+ # @example Use table_guard instead (RECOMMENDED)
29
+ # plugin :rodauth do
30
+ # enable :base, :verify_account, :otp, :table_guard
31
+ # table_guard_sequel_mode :migration
32
+ # end
33
+ class Migration
34
+ attr_reader :features, :prefix, :db_adapter, :db
35
+
36
+ # Initialize the migration generator
37
+ #
38
+ # @param features [Array<Symbol>] List of Rodauth features to generate tables for
39
+ # @param prefix [String] Table name prefix (default: "account")
40
+ # @param db_adapter [Symbol] Database adapter (:postgresql, :mysql2, :sqlite3)
41
+ # @param db [Sequel::Database] Sequel database connection
42
+ def initialize(features:, prefix: nil, db_adapter: nil, db: nil)
43
+ @features = Array(features).map(&:to_sym)
44
+ @prefix = prefix
45
+ @db_adapter = db_adapter&.to_sym
46
+ @db = db || create_mock_db
47
+
48
+ validate_features!
49
+ validate_feature_templates!
50
+ end
51
+
52
+ # Generate the migration content
53
+ #
54
+ # @return [String] Complete migration file content
55
+ def generate
56
+ features
57
+ .map { |feature| load_template(feature) }
58
+ .map { |content| evaluate_erb(content) }
59
+ .join("\n")
60
+ end
61
+
62
+ # Execute CREATE TABLE operations directly against the database
63
+ #
64
+ # This evaluates the ERB templates and executes the resulting
65
+ # Sequel migration code against the provided database connection.
66
+ #
67
+ # @param db [Sequel::Database] Database connection
68
+ # @return [void]
69
+ def execute_create_tables(db)
70
+ # Update the db reference for template binding
71
+ @db = db
72
+
73
+ # Generate migration code from ERB templates
74
+ migration_code = generate
75
+
76
+ # Load Sequel's migration extension
77
+ require 'sequel/extensions/migration'
78
+
79
+ # Wrap in Sequel.migration block and execute
80
+ # NOTE: Using eval here is appropriate because:
81
+ # 1. The code is generated from our own trusted ERB templates
82
+ # 2. This is how Sequel migrations work - they're Ruby DSL code
83
+ # 3. The alternative would require re-implementing Sequel's migration DSL
84
+ migration = eval(<<~RUBY, binding, __FILE__, __LINE__ + 1)
85
+ Sequel.migration do
86
+ up do
87
+ #{migration_code}
88
+ end
89
+ end
90
+ RUBY
91
+
92
+ # Apply the migration
93
+ migration.apply(db, :up)
94
+ end
95
+
96
+ # Get the migration name
97
+ #
98
+ # @return [String] Migration name
99
+ def migration_name
100
+ parts = ['create_rodauth']
101
+ parts << prefix if prefix && prefix != 'account'
102
+ parts.concat(features)
103
+ parts.join('_')
104
+ end
105
+
106
+ # Check if an ERB template exists for a given feature
107
+ #
108
+ # @param feature [Symbol] Feature name
109
+ # @return [Boolean] True if template exists
110
+ def self.template_exists?(feature)
111
+ template_path = File.join(__dir__, 'migration', 'sequel', "#{feature}.erb")
112
+ File.exist?(template_path)
113
+ end
114
+
115
+ private
116
+
117
+ def validate_features!
118
+ return if features.any?
119
+
120
+ raise ArgumentError, 'No features specified'
121
+ end
122
+
123
+ def validate_feature_templates!
124
+ features.each do |feature|
125
+ template_path = File.join(template_directory, "#{feature}.erb")
126
+ raise ArgumentError, "No migration template for feature: #{feature}" unless File.exist?(template_path)
127
+ end
128
+ end
129
+
130
+ def create_mock_db
131
+ adapter = @db_adapter || :postgres
132
+ MockSequelDatabase.new(adapter)
133
+ end
134
+
135
+ def load_template(feature)
136
+ template_path = File.join(template_directory, "#{feature}.erb")
137
+ File.read(template_path)
138
+ end
139
+
140
+ def evaluate_erb(content)
141
+ ERB.new(content, trim_mode: '-').result(binding)
142
+ end
143
+
144
+ def template_directory
145
+ File.join(__dir__, 'migration', 'sequel')
146
+ end
147
+
148
+ def table_prefix
149
+ (@prefix || 'account').to_s
150
+ end
151
+
152
+ # Helper method for templates to pluralize table names
153
+ def pluralize(str)
154
+ inflector.pluralize(str)
155
+ end
156
+
157
+ # Helper method for JSON column type based on database type
158
+ def json_type
159
+ case db.database_type
160
+ when :postgres
161
+ :jsonb
162
+ when :sqlite, :mysql
163
+ :json
164
+ else
165
+ String
166
+ end
167
+ end
168
+
169
+ # Cached inflector instance
170
+ def inflector
171
+ @inflector ||= Dry::Inflector.new
172
+ end
173
+
174
+ # Mock database object for Sequel templates when no real db is provided
175
+ class MockSequelDatabase
176
+ attr_reader :database_type
177
+
178
+ def initialize(adapter = :postgres)
179
+ @database_type = adapter
180
+ end
181
+
182
+ def supports_partial_indexes?
183
+ %i[postgres sqlite].include?(database_type)
184
+ end
185
+ end
186
+ end
187
+ end
188
+ end
@@ -0,0 +1,9 @@
1
+ # lib/rodauth/tools/version.rb
2
+ #
3
+ # frozen_string_literal: true
4
+
5
+ module Rodauth
6
+ module Tools
7
+ VERSION = '0.3.0'
8
+ end
9
+ end
@@ -0,0 +1,29 @@
1
+ # lib/rodauth/tools.rb
2
+ #
3
+ # frozen_string_literal: true
4
+
5
+ # Rodauth must be loaded before our external features can be defined
6
+ begin
7
+ require 'rodauth'
8
+ rescue LoadError
9
+ # Rodauth not available - external features won't be loaded
10
+ end
11
+
12
+ require_relative 'tools/version'
13
+ require_relative 'tools/migration'
14
+ require_relative 'tools/console_helpers'
15
+
16
+ # Load rodauth-tools utilities (only if Rodauth is available)
17
+ if defined?(Rodauth)
18
+ require_relative 'table_inspector'
19
+ require_relative 'sequel_generator'
20
+ require_relative 'features/table_guard'
21
+ require_relative 'features/external_identity'
22
+ end
23
+
24
+ module Rodauth
25
+ # Tools module contains framework-agnostic Rodauth utilities
26
+ module Tools
27
+ class Error < StandardError; end
28
+ end
29
+ end