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,134 @@
1
+ # lib/rodauth/template_inspector.rb
2
+ #
3
+ # frozen_string_literal: true
4
+
5
+ require 'erb'
6
+ require 'dry/inflector'
7
+
8
+ module Rodauth
9
+ # Inspects ERB templates to extract table information
10
+ #
11
+ # This module solves the "hidden tables" problem where ERB templates create
12
+ # tables that don't have corresponding *_table methods in Rodauth features.
13
+ # For example, base.erb creates account_statuses and account_password_hashes
14
+ # tables, but only accounts_table method exists.
15
+ #
16
+ # By evaluating ERB templates, we can discover ALL tables that will be created,
17
+ # which is essential for generating complete DROP statements.
18
+ module TemplateInspector
19
+ # Extract table names from a single ERB template
20
+ #
21
+ # @param feature [Symbol] Feature name (e.g., :base, :verify_account)
22
+ # @param table_prefix [String] Table prefix to use (default: 'account')
23
+ # @param db_type [Symbol] Database type for conditional logic (default: :postgres)
24
+ # @return [Array<Symbol>] Array of table names that will be created
25
+ def self.extract_tables_from_template(feature, table_prefix: 'account', db_type: :postgres)
26
+ template_path = template_path_for_feature(feature)
27
+ return [] unless File.exist?(template_path)
28
+
29
+ template_content = File.read(template_path)
30
+
31
+ # Create binding context with necessary methods
32
+ context = BindingContext.new(table_prefix, db_type)
33
+
34
+ # Evaluate ERB template
35
+ begin
36
+ rendered = ERB.new(template_content, trim_mode: '-').result(context.get_binding)
37
+ rescue StandardError => e
38
+ warn "Failed to evaluate template for #{feature}: #{e.message}"
39
+ return []
40
+ end
41
+
42
+ # Extract table names from create_table calls
43
+ # Matches: create_table(:table_name) or create_table?(:table_name)
44
+ tables = rendered.scan(/create_table\??[:(\s]+:?(\w+)/).flatten
45
+
46
+ tables.map(&:to_sym).uniq
47
+ end
48
+
49
+ # Get all tables for a set of features
50
+ #
51
+ # @param features [Array<Symbol>] Feature names
52
+ # @param table_prefix [String] Table prefix to use
53
+ # @param db_type [Symbol] Database type
54
+ # @return [Array<Symbol>] Array of all table names across features
55
+ def self.all_tables_for_features(features, table_prefix: 'account', db_type: :postgres)
56
+ tables = []
57
+
58
+ features.each do |feature|
59
+ feature_tables = extract_tables_from_template(
60
+ feature,
61
+ table_prefix: table_prefix,
62
+ db_type: db_type
63
+ )
64
+ tables.concat(feature_tables)
65
+ end
66
+
67
+ tables.uniq
68
+ end
69
+
70
+ # Get template path for a feature
71
+ #
72
+ # @param feature [Symbol] Feature name
73
+ # @return [String] Absolute path to ERB template
74
+ def self.template_path_for_feature(feature)
75
+ File.join(__dir__, 'tools', 'migration', 'sequel', "#{feature}.erb")
76
+ end
77
+
78
+ # Binding context for ERB evaluation
79
+ #
80
+ # Provides minimal methods needed for ERB template evaluation without
81
+ # requiring a full database connection or Rodauth instance.
82
+ class BindingContext
83
+ attr_reader :table_prefix
84
+
85
+ def initialize(table_prefix, db_type)
86
+ @table_prefix = table_prefix
87
+ @db_type = db_type
88
+ @inflector = Dry::Inflector.new
89
+ end
90
+
91
+ # Pluralize a word using dry-inflector
92
+ def pluralize(word)
93
+ @inflector.pluralize(word.to_s)
94
+ end
95
+
96
+ # Mock database object for template evaluation
97
+ #
98
+ # Templates check db.database_type and db.supports_partial_indexes?
99
+ # to generate database-specific code.
100
+ def db
101
+ @db ||= MockDatabase.new(@db_type)
102
+ end
103
+
104
+ # Get binding for ERB evaluation
105
+ def get_binding
106
+ binding
107
+ end
108
+
109
+ # Mock Sequel database for ERB templates
110
+ class MockDatabase
111
+ attr_reader :database_type
112
+
113
+ def initialize(db_type)
114
+ @database_type = db_type
115
+ end
116
+
117
+ def supports_partial_indexes?
118
+ # PostgreSQL and SQLite support partial indexes
119
+ %i[postgres sqlite].include?(@database_type)
120
+ end
121
+
122
+ # Stub other methods that might be called in templates
123
+ def method_missing(_method, *_args)
124
+ # Return a safe default for unknown methods
125
+ nil
126
+ end
127
+
128
+ def respond_to_missing?(_method, _include_private = false)
129
+ true
130
+ end
131
+ end
132
+ end
133
+ end
134
+ end
@@ -0,0 +1,158 @@
1
+ # lib/rodauth/tools/console_helpers.rb
2
+ #
3
+ # frozen_string_literal: true
4
+
5
+ module Rodauth
6
+ module Tools
7
+ # Console helper methods for inspecting Rodauth table configuration
8
+ #
9
+ # Usage in console:
10
+ # require 'rodauth/tools/console_helpers'
11
+ # include Rodauth::Tools::ConsoleHelpers
12
+ # rodauth.table_configuration
13
+ #
14
+ # Or use the convenience loader:
15
+ # Rodauth::Tools::ConsoleHelpers.load!(rodauth_instance)
16
+ module ConsoleHelpers
17
+ # Get or create a Rodauth instance
18
+ #
19
+ # Override this method in your console script to provide the actual instance
20
+ def rodauth
21
+ @rodauth ||= raise NotImplementedError, 'You must define a `rodauth` method that returns a Rodauth instance'
22
+ end
23
+
24
+ # Get discovered table configuration
25
+ def config
26
+ rodauth.table_configuration
27
+ end
28
+
29
+ # Get missing tables
30
+ def missing
31
+ rodauth.missing_tables
32
+ end
33
+
34
+ # List all required table names
35
+ def tables
36
+ rodauth.list_all_required_tables
37
+ end
38
+
39
+ # Get detailed status for each table
40
+ def status
41
+ rodauth.table_status
42
+ end
43
+
44
+ # Access database connection
45
+ def db
46
+ rodauth.db if rodauth.respond_to?(:db)
47
+ end
48
+
49
+ # Pretty-print table configuration
50
+ def show_config
51
+ puts "\n=== Table Configuration ==="
52
+ config.each do |method, info|
53
+ puts "\n#{method}:"
54
+ puts " Table: #{info[:name]}"
55
+ puts " Feature: #{info[:feature]}"
56
+ puts " Type: #{info[:structure][:type]}"
57
+ puts " Exists: #{rodauth.table_exists?(info[:name])}"
58
+ end
59
+ nil
60
+ end
61
+
62
+ # Pretty-print missing tables
63
+ def show_missing
64
+ puts "\n=== Missing Tables ==="
65
+ if missing.empty?
66
+ puts '✓ All tables exist!'
67
+ else
68
+ missing.each do |info|
69
+ puts "✗ #{info[:table]} (feature: #{info[:feature]})"
70
+ end
71
+ end
72
+ nil
73
+ end
74
+
75
+ # Pretty-print table status
76
+ def show_status
77
+ puts "\n=== Table Status ==="
78
+ status.each do |info|
79
+ marker = info[:exists] ? '✓' : '✗'
80
+ puts "#{marker} #{info[:table].to_s.ljust(30)} (#{info[:feature]})"
81
+ end
82
+ nil
83
+ end
84
+
85
+ # Create missing tables immediately
86
+ def create_tables!
87
+ puts "\n=== Creating Missing Tables ==="
88
+ missing_list = missing
89
+ if missing_list.empty?
90
+ puts '✓ All tables already exist!'
91
+ return
92
+ end
93
+
94
+ generator = Rodauth::SequelGenerator.new(missing_list, rodauth)
95
+ generator.execute_creates(db)
96
+ puts "✓ Created #{missing_list.size} table(s)"
97
+ show_status
98
+ nil
99
+ end
100
+
101
+ # Display generated migration code
102
+ def show_migration
103
+ puts "\n=== Generated Migration ==="
104
+ missing_list = missing
105
+ if missing_list.empty?
106
+ puts '✓ All tables exist - no migration needed'
107
+ return
108
+ end
109
+
110
+ generator = Rodauth::SequelGenerator.new(missing_list, rodauth)
111
+ puts generator.generate_migration
112
+ nil
113
+ end
114
+
115
+ # Show help message
116
+ def help
117
+ puts <<~HELP
118
+
119
+ 🔍 Rodauth Console Helpers
120
+ ============================
121
+
122
+ rodauth # Get Rodauth instance
123
+ config # Get discovered table configuration
124
+ missing # Get missing tables
125
+ tables # List all required table names
126
+ status # Get detailed status for each table
127
+ db # Access Sequel database connection
128
+
129
+ show_config # Pretty-print table configuration
130
+ show_missing # Pretty-print missing tables
131
+ show_status # Pretty-print table status
132
+ create_tables! # Create all missing tables
133
+ show_migration # Display generated migration code
134
+
135
+ help # Show this help
136
+
137
+ Examples:
138
+ ---------
139
+ config.keys # See all table methods
140
+ missing.size # How many tables are missing?
141
+ rodauth.table_exists?(:accounts) # Check specific table
142
+ db.tables # See what's in the database
143
+
144
+ HELP
145
+ nil
146
+ end
147
+
148
+ # Class method to extend a context with helpers and set up rodauth method
149
+ #
150
+ # @param rodauth_instance [Rodauth::Auth] Rodauth instance
151
+ # @return [Module] Extended module
152
+ def self.extended(base)
153
+ puts "\n✓ Rodauth console helpers loaded!"
154
+ base.help if base.respond_to?(:help)
155
+ end
156
+ end
157
+ end
158
+ end
@@ -0,0 +1,9 @@
1
+ <%# lib/rodauth/tools/migration/sequel/audit_logging.erb %>
2
+
3
+ # Used by the account expiration feature
4
+ create_table(:<%= table_prefix %>_activity_times) do
5
+ foreign_key :id, :<%= pluralize(table_prefix) %>, primary_key: true, type: :Bignum
6
+ DateTime :last_activity_at, null: false
7
+ DateTime :last_login_at, null: false
8
+ DateTime :expired_at
9
+ end
@@ -0,0 +1,10 @@
1
+ <%# lib/rodauth/tools/migration/sequel/audit_logging.erb %>
2
+
3
+ # Used by the active sessions feature
4
+ create_table(:<%= table_prefix %>_active_session_keys) do
5
+ foreign_key :<%= table_prefix %>_id, :<%= pluralize(table_prefix) %>, type: :Bignum
6
+ String :session_id
7
+ Time :created_at, null: false, default: Sequel::CURRENT_TIMESTAMP
8
+ Time :last_use, null: false, default: Sequel::CURRENT_TIMESTAMP
9
+ primary_key [:<%= table_prefix %>_id, :session_id]
10
+ end
@@ -0,0 +1,12 @@
1
+ <%# lib/rodauth/tools/migration/sequel/audit_logging.erb %>
2
+
3
+ # Used by the audit logging feature
4
+ create_table(:<%= table_prefix %>_authentication_audit_logs) do
5
+ primary_key :id, type: :Bignum
6
+ foreign_key :<%= table_prefix %>_id, :<%= pluralize(table_prefix) %>, null: false, type: :Bignum
7
+ DateTime :at, null: false, default: Sequel::CURRENT_TIMESTAMP
8
+ String :message, null: false
9
+ column :metadata, <%= json_type %>
10
+ index [:<%= table_prefix %>_id, :at], name: :audit_<%= table_prefix %>_at_idx
11
+ index :at, name: :audit_at_idx
12
+ end
@@ -0,0 +1,41 @@
1
+ <%# lib/rodauth/tools/migration/sequel/base.erb %>
2
+
3
+ db = self
4
+ <% if db.database_type == :postgres -%>
5
+ begin
6
+ run "CREATE EXTENSION IF NOT EXISTS citext"
7
+ rescue NoMethodError # migration is being reverted
8
+ end
9
+ <% end -%>
10
+
11
+ # Sequel extension
12
+ extension :date_arithmetic
13
+
14
+ # Used by the account verification and close account features
15
+ create_table(:<%= table_prefix %>_statuses) do
16
+ Integer :id, primary_key: true
17
+ String :name, null: false, unique: true
18
+ end
19
+ from(:<%= table_prefix %>_statuses).import([:id, :name], [[1, 'Unverified'], [2, 'Verified'], [3, 'Closed']])
20
+
21
+ create_table(:<%= table_prefix %>_password_hashes) do
22
+ foreign_key :id, :<%= pluralize(table_prefix) %>, primary_key: true, type: :Bignum
23
+ String :password_hash, null: false
24
+ end
25
+
26
+ create_table(:<%= pluralize(table_prefix) %>) do
27
+ primary_key :id, type: :Bignum
28
+ foreign_key :status_id, :<%= table_prefix %>_statuses, null: false, default: 1
29
+ <% if db.database_type == :postgres -%>
30
+ citext :email, null: false
31
+ constraint :valid_email, email: /^[^,;@ \r\n]+@[^,@; \r\n]+\.[^,@; \r\n]+$/
32
+ <% else -%>
33
+ String :email, null: false
34
+ <% end -%>
35
+ <% if db.supports_partial_indexes? -%>
36
+ index :email, unique: true, where: {status_id: [1, 2]}
37
+ <% else -%>
38
+ index :email, unique: true
39
+ <% end -%>
40
+ String :password_hash, null: true
41
+ end
@@ -0,0 +1,8 @@
1
+ <%# lib/rodauth/tools/migration/sequel/audit_logging.erb %>
2
+
3
+ # Used by the disallow password reuse feature
4
+ create_table(:<%= table_prefix %>_previous_password_hashes) do
5
+ primary_key :id, type: :Bignum
6
+ foreign_key :<%= table_prefix %>_id, :<%= pluralize(table_prefix) %>, type: :Bignum
7
+ String :password_hash, null: false
8
+ end
@@ -0,0 +1,17 @@
1
+ <%# lib/rodauth/tools/migration/sequel/audit_logging.erb %>
2
+
3
+ # Used by the email auth feature
4
+ deadline_opts = proc do |days|
5
+ if db.database_type == :mysql
6
+ {null: false}
7
+ else
8
+ {null: false, default: Sequel.date_add(Sequel::CURRENT_TIMESTAMP, days: days)}
9
+ end
10
+ end
11
+
12
+ create_table(:<%= table_prefix %>_email_auth_keys) do
13
+ foreign_key :id, :<%= pluralize(table_prefix) %>, primary_key: true, type: :Bignum
14
+ String :key, null: false
15
+ DateTime :deadline, deadline_opts[1]
16
+ DateTime :email_last_sent, null: false, default: Sequel::CURRENT_TIMESTAMP
17
+ end
@@ -0,0 +1,18 @@
1
+ <%# lib/rodauth/tools/migration/sequel/audit_logging.erb %>
2
+
3
+ # Used by the jwt refresh feature
4
+ deadline_opts = proc do |days|
5
+ if db.database_type == :mysql
6
+ {null: false}
7
+ else
8
+ {null: false, default: Sequel.date_add(Sequel::CURRENT_TIMESTAMP, days: days)}
9
+ end
10
+ end
11
+
12
+ create_table(:<%= table_prefix %>_jwt_refresh_keys) do
13
+ primary_key :id, type: :Bignum
14
+ foreign_key :<%= table_prefix %>_id, :<%= pluralize(table_prefix) %>, null: false, type: :Bignum
15
+ String :key, null: false
16
+ DateTime :deadline, deadline_opts[1]
17
+ index :<%= table_prefix %>_id, name: :<%= table_prefix %>_jwt_rk_<%= table_prefix %>_id_idx
18
+ end
@@ -0,0 +1,21 @@
1
+ <%# lib/rodauth/tools/migration/sequel/audit_logging.erb %>
2
+
3
+ # Used by the lockout feature
4
+ deadline_opts = proc do |days|
5
+ if db.database_type == :mysql
6
+ {null: false}
7
+ else
8
+ {null: false, default: Sequel.date_add(Sequel::CURRENT_TIMESTAMP, days: days)}
9
+ end
10
+ end
11
+
12
+ create_table(:<%= table_prefix %>_login_failures) do
13
+ foreign_key :id, :<%= pluralize(table_prefix) %>, primary_key: true, type: :Bignum
14
+ Integer :number, null: false, default: 1
15
+ end
16
+ create_table(:<%= table_prefix %>_lockouts) do
17
+ foreign_key :id, :<%= pluralize(table_prefix) %>, primary_key: true, type: :Bignum
18
+ String :key, null: false
19
+ DateTime :deadline, deadline_opts[1]
20
+ DateTime :email_last_sent
21
+ end
@@ -0,0 +1,9 @@
1
+ <%# lib/rodauth/tools/migration/sequel/audit_logging.erb %>
2
+
3
+ # Used by the otp feature
4
+ create_table(:<%= table_prefix %>_otp_keys) do
5
+ foreign_key :id, :<%= pluralize(table_prefix) %>, primary_key: true, type: :Bignum
6
+ String :key, null: false
7
+ Integer :num_failures, null: false, default: 0
8
+ Time :last_use, null: false, default: Sequel::CURRENT_TIMESTAMP
9
+ end
@@ -0,0 +1,8 @@
1
+ <%# lib/rodauth/tools/migration/sequel/audit_logging.erb %>
2
+
3
+ # Used by the otp_unlock feature
4
+ create_table(:<%= table_prefix %>_otp_unlocks) do
5
+ foreign_key :id, :<%= pluralize(table_prefix) %>, primary_key: true, type: :Bignum
6
+ Integer :num_successes, null: false, default: 1
7
+ Time :next_auth_attempt_after, null: false, default: Sequel::CURRENT_TIMESTAMP
8
+ end
@@ -0,0 +1,7 @@
1
+ <%# lib/rodauth/tools/migration/sequel/audit_logging.erb %>
2
+
3
+ # Used by the password expiration feature
4
+ create_table(:<%= table_prefix %>_password_change_times) do
5
+ foreign_key :id, :<%= pluralize(table_prefix) %>, primary_key: true, type: :Bignum
6
+ DateTime :changed_at, null: false, default: Sequel::CURRENT_TIMESTAMP
7
+ end
@@ -0,0 +1,8 @@
1
+ <%# lib/rodauth/tools/migration/sequel/audit_logging.erb %>
2
+
3
+ # Used by the recovery codes feature
4
+ create_table(:<%= table_prefix %>_recovery_codes) do
5
+ foreign_key :id, :<%= pluralize(table_prefix) %>, type: :Bignum
6
+ String :code
7
+ primary_key [:id, :code]
8
+ end
@@ -0,0 +1,16 @@
1
+ <%# lib/rodauth/tools/migration/sequel/audit_logging.erb %>
2
+
3
+ # Used by the remember me feature
4
+ deadline_opts = proc do |days|
5
+ if db.database_type == :mysql
6
+ {null: false}
7
+ else
8
+ {null: false, default: Sequel.date_add(Sequel::CURRENT_TIMESTAMP, days: days)}
9
+ end
10
+ end
11
+
12
+ create_table(:<%= table_prefix %>_remember_keys) do
13
+ foreign_key :id, :<%= pluralize(table_prefix) %>, primary_key: true, type: :Bignum
14
+ String :key, null: false
15
+ DateTime :deadline, deadline_opts[14]
16
+ end
@@ -0,0 +1,17 @@
1
+ <%# lib/rodauth/tools/migration/sequel/audit_logging.erb %>
2
+
3
+ # Used by the password reset feature
4
+ deadline_opts = proc do |days|
5
+ if db.database_type == :mysql
6
+ {null: false}
7
+ else
8
+ {null: false, default: Sequel.date_add(Sequel::CURRENT_TIMESTAMP, days: days)}
9
+ end
10
+ end
11
+
12
+ create_table(:<%= table_prefix %>_password_reset_keys) do
13
+ foreign_key :id, :<%= pluralize(table_prefix) %>, primary_key: true, type: :Bignum
14
+ String :key, null: false
15
+ DateTime :deadline, deadline_opts[1]
16
+ DateTime :email_last_sent, null: false, default: Sequel::CURRENT_TIMESTAMP
17
+ end
@@ -0,0 +1,7 @@
1
+ <%# lib/rodauth/tools/migration/sequel/audit_logging.erb %>
2
+
3
+ # Used by the single session feature
4
+ create_table(:<%= table_prefix %>_session_keys) do
5
+ foreign_key :id, :<%= pluralize(table_prefix) %>, primary_key: true, type: :Bignum
6
+ String :key, null: false
7
+ end
@@ -0,0 +1,10 @@
1
+ <%# lib/rodauth/tools/migration/sequel/audit_logging.erb %>
2
+
3
+ # Used by the sms codes feature
4
+ create_table(:<%= table_prefix %>_sms_codes) do
5
+ foreign_key :id, :<%= pluralize(table_prefix) %>, primary_key: true, type: :Bignum
6
+ String :phone_number, null: false
7
+ Integer :num_failures
8
+ String :code
9
+ DateTime :code_issued_at, null: false, default: Sequel::CURRENT_TIMESTAMP
10
+ end
@@ -0,0 +1,9 @@
1
+ <%# lib/rodauth/tools/migration/sequel/audit_logging.erb %>
2
+
3
+ # Used by the account verification feature
4
+ create_table(:<%= table_prefix %>_verification_keys) do
5
+ foreign_key :id, :<%= pluralize(table_prefix) %>, primary_key: true, type: :Bignum
6
+ String :key, null: false
7
+ DateTime :requested_at, null: false, default: Sequel::CURRENT_TIMESTAMP
8
+ DateTime :email_last_sent, null: false, default: Sequel::CURRENT_TIMESTAMP
9
+ end
@@ -0,0 +1,17 @@
1
+ <%# lib/rodauth/tools/migration/sequel/audit_logging.erb %>
2
+
3
+ # Used by the verify login change feature
4
+ deadline_opts = proc do |days|
5
+ if db.database_type == :mysql
6
+ {null: false}
7
+ else
8
+ {null: false, default: Sequel.date_add(Sequel::CURRENT_TIMESTAMP, days: days)}
9
+ end
10
+ end
11
+
12
+ create_table(:<%= table_prefix %>_login_change_keys) do
13
+ foreign_key :id, :<%= pluralize(table_prefix) %>, primary_key: true, type: :Bignum
14
+ String :key, null: false
15
+ String :login, null: false
16
+ DateTime :deadline, deadline_opts[1]
17
+ end
@@ -0,0 +1,15 @@
1
+ <%# lib/rodauth/tools/migration/sequel/audit_logging.erb %>
2
+
3
+ # Used by the webauthn feature
4
+ create_table(:<%= table_prefix %>_webauthn_user_ids) do
5
+ foreign_key :id, :<%= pluralize(table_prefix) %>, primary_key: true, type: :Bignum
6
+ String :webauthn_id, null: false
7
+ end
8
+ create_table(:<%= table_prefix %>_webauthn_keys) do
9
+ foreign_key :<%= table_prefix %>_id, :<%= pluralize(table_prefix) %>, type: :Bignum
10
+ String :webauthn_id
11
+ String :public_key, null: false
12
+ Integer :sign_count, null: false
13
+ Time :last_use, null: false, default: Sequel::CURRENT_TIMESTAMP
14
+ primary_key [:<%= table_prefix %>_id, :webauthn_id]
15
+ end