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,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,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
|