rodauth-rails 1.3.1 → 1.4.2

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 (54) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +26 -0
  3. data/README.md +10 -103
  4. data/lib/generators/rodauth/install_generator.rb +9 -10
  5. data/lib/generators/rodauth/migration/{account_expiration.erb → active_record/account_expiration.erb} +0 -0
  6. data/lib/generators/rodauth/migration/{active_sessions.erb → active_record/active_sessions.erb} +0 -0
  7. data/lib/generators/rodauth/migration/{audit_logging.erb → active_record/audit_logging.erb} +0 -0
  8. data/lib/generators/rodauth/migration/{base.erb → active_record/base.erb} +0 -0
  9. data/lib/generators/rodauth/migration/{disallow_password_reuse.erb → active_record/disallow_password_reuse.erb} +1 -1
  10. data/lib/generators/rodauth/migration/{email_auth.erb → active_record/email_auth.erb} +0 -0
  11. data/lib/generators/rodauth/migration/{jwt_refresh.erb → active_record/jwt_refresh.erb} +0 -0
  12. data/lib/generators/rodauth/migration/{lockout.erb → active_record/lockout.erb} +0 -0
  13. data/lib/generators/rodauth/migration/{otp.erb → active_record/otp.erb} +0 -0
  14. data/lib/generators/rodauth/migration/{password_expiration.erb → active_record/password_expiration.erb} +0 -0
  15. data/lib/generators/rodauth/migration/{recovery_codes.erb → active_record/recovery_codes.erb} +0 -0
  16. data/lib/generators/rodauth/migration/{remember.erb → active_record/remember.erb} +0 -0
  17. data/lib/generators/rodauth/migration/{reset_password.erb → active_record/reset_password.erb} +0 -0
  18. data/lib/generators/rodauth/migration/{single_session.erb → active_record/single_session.erb} +0 -0
  19. data/lib/generators/rodauth/migration/{sms_codes.erb → active_record/sms_codes.erb} +0 -0
  20. data/lib/generators/rodauth/migration/{verify_account.erb → active_record/verify_account.erb} +0 -0
  21. data/lib/generators/rodauth/migration/{verify_login_change.erb → active_record/verify_login_change.erb} +0 -0
  22. data/lib/generators/rodauth/migration/{webauthn.erb → active_record/webauthn.erb} +0 -0
  23. data/lib/generators/rodauth/migration/sequel/account_expiration.erb +7 -0
  24. data/lib/generators/rodauth/migration/sequel/active_sessions.erb +8 -0
  25. data/lib/generators/rodauth/migration/sequel/audit_logging.erb +17 -0
  26. data/lib/generators/rodauth/migration/sequel/base.erb +25 -0
  27. data/lib/generators/rodauth/migration/sequel/disallow_password_reuse.erb +6 -0
  28. data/lib/generators/rodauth/migration/sequel/email_auth.erb +7 -0
  29. data/lib/generators/rodauth/migration/sequel/jwt_refresh.erb +8 -0
  30. data/lib/generators/rodauth/migration/sequel/lockout.erb +11 -0
  31. data/lib/generators/rodauth/migration/sequel/otp.erb +7 -0
  32. data/lib/generators/rodauth/migration/sequel/password_expiration.erb +5 -0
  33. data/lib/generators/rodauth/migration/sequel/recovery_codes.erb +6 -0
  34. data/lib/generators/rodauth/migration/sequel/remember.erb +6 -0
  35. data/lib/generators/rodauth/migration/sequel/reset_password.erb +7 -0
  36. data/lib/generators/rodauth/migration/sequel/single_session.erb +5 -0
  37. data/lib/generators/rodauth/migration/sequel/sms_codes.erb +8 -0
  38. data/lib/generators/rodauth/migration/sequel/verify_account.erb +7 -0
  39. data/lib/generators/rodauth/migration/sequel/verify_login_change.erb +7 -0
  40. data/lib/generators/rodauth/migration/sequel/webauthn.erb +13 -0
  41. data/lib/generators/rodauth/migration_generator.rb +89 -9
  42. data/lib/generators/rodauth/templates/app/mailers/rodauth_mailer.rb +38 -28
  43. data/lib/generators/rodauth/templates/app/misc/rodauth_main.rb +6 -6
  44. data/lib/generators/rodauth/templates/app/models/account.rb +8 -0
  45. data/lib/generators/rodauth/templates/app/views/rodauth_mailer/verify_login_change.text.erb +2 -2
  46. data/lib/generators/rodauth/templates/db/migrate/create_rodauth.rb +8 -0
  47. data/lib/rodauth/rails/middleware.rb +9 -0
  48. data/lib/rodauth/rails/model.rb +2 -97
  49. data/lib/rodauth/rails/version.rb +1 -1
  50. data/lib/rodauth/rails.rb +2 -1
  51. data/rodauth-rails.gemspec +1 -0
  52. metadata +52 -22
  53. data/lib/generators/rodauth/migration_helpers.rb +0 -77
  54. data/lib/rodauth/rails/model/associations.rb +0 -195
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4e268594e5890725cbba25ee24ab158df204ada3789aeebe428b737307128d9e
4
- data.tar.gz: dca778863032dc428ac44b5feca48fe430ef55ea64f4e10050293d1c1a3d95c6
3
+ metadata.gz: 9a42fbc8522feb0a7d18227ca1cb3d79448c6d5d824befb040699afdb69318ea
4
+ data.tar.gz: 4d60492cc1ea2acf8176ffb6a05c6f0aba7deda63d1541da42de7f558e55b6e6
5
5
  SHA512:
6
- metadata.gz: 9008b2381959811820eca625f68c5df7ec2021319ceb2c7bdf6c75412f32ea51f01ffabd716da73f6565e263e0a74699c2c940a70296adf0b9c0b8576ef8e3de
7
- data.tar.gz: 4b0d70d43ff624b610bcded93a528b089d103f11975b7fdec9c8bd38b1f81b5c003de2fa1e4068f2c2006841f91783044280fa575ec5951b2ae319f4836a49b7
6
+ metadata.gz: 8782debac4a0b3bea9bb1849ab0817966f62c3bcf0cf352c2cf83d68e0fc35bce83a321e8c49f96d027450b87d6c196afd1e451a6e79e53cd35328dd84893d8c
7
+ data.tar.gz: 24de3dec4d061a9421de53062d75959d67091aff43dc26bf7f7fd749664e2288a4a9c198f24fa4ce8890b0c88f38c294d85a565b09aae330c0bcf7674242f112
data/CHANGELOG.md CHANGED
@@ -1,3 +1,29 @@
1
+ ## 1.4.2 (2022-05-15)
2
+
3
+ * Stop passing email addresses in mailer arguments on verifying login change (@janko)
4
+
5
+ * Extract finding account into a method in the generated mailer (@janko)
6
+
7
+ * Make generated Action Mailer integration work with secondary Rodauth configurations (@janko)
8
+
9
+ * Include `Rodauth::Rails.model` in generated Sequel account model as well (@janko)
10
+
11
+ ## 1.4.1 (2022-05-08)
12
+
13
+ * Deprecate `Rodauth::Rails::Model` constant (@janko)
14
+
15
+ * Remove `Rodauth::Rails::Auth#associations` in favour of new association registration API (@janko)
16
+
17
+ * Extract model mixin into the rodauth-model gem (@janko)
18
+
19
+ ## 1.4.0 (2022-05-04)
20
+
21
+ * Move association definitions to `#associations` Rodauth method, allowing external features to extend them (@janko)
22
+
23
+ * Add Sequel support for generating database migrations, model, and mailer (@janko)
24
+
25
+ * Skip calling Rodauth app on asset requests when using Sprockets or Propshaft (@janko)
26
+
1
27
  ## 1.3.1 (2022-04-22)
2
28
 
3
29
  * Ensure response status is logged when calling a halting rodauth method inside a controller (@janko)
data/README.md CHANGED
@@ -484,21 +484,19 @@ end
484
484
 
485
485
  ## Model
486
486
 
487
- The `Rodauth::Rails::Model` mixin can be included into the account model, which
488
- defines a password attribute and associations for tables used by enabled
489
- authentication features.
487
+ The [rodauth-model] gem provides a `Rodauth::Model` mixin that can be included
488
+ into the account model, which defines a password attribute and associations for
489
+ tables used by enabled authentication features.
490
490
 
491
491
  ```rb
492
- class Account < ApplicationRecord
492
+ class Account < ActiveRecord::Base # Sequel::Model
493
493
  include Rodauth::Rails.model # or `Rodauth::Rails.model(:admin)`
494
494
  end
495
495
  ```
496
496
 
497
- ### Password attribute
498
-
499
- Regardless of whether you're storing the password hash in a column in the
500
- accounts table, or in a separate table, the `#password` attribute can be used
501
- to set or clear the password hash.
497
+ The password attribute can be used to set or clear the password hash. It
498
+ handles both storing the password hash in a column on the accounts table, or in
499
+ a separate table.
502
500
 
503
501
  ```rb
504
502
  account = Account.create!(email: "user@example.com", password: "secret")
@@ -514,105 +512,14 @@ account.password = nil # clears password hash
514
512
  account.password_hash #=> nil
515
513
  ```
516
514
 
517
- Note that the password attribute doesn't come with validations, making it
518
- unsuitable for forms. It was primarily intended to allow easily creating
519
- accounts in development console and in tests.
520
-
521
- ### Associations
522
-
523
- The `Rodauth::Rails::Model` mixin defines associations for Rodauth tables
524
- associated to the accounts table:
515
+ The associations are defined for tables used by enabled authentication features:
525
516
 
526
517
  ```rb
527
518
  account.remember_key #=> #<Account::RememberKey> (record from `account_remember_keys` table)
528
519
  account.active_session_keys #=> [#<Account::ActiveSessionKey>,...] (records from `account_active_session_keys` table)
529
520
  ```
530
521
 
531
- You can also reference the associated models directly:
532
-
533
- ```rb
534
- # model referencing the `account_authentication_audit_logs` table
535
- Account::AuthenticationAuditLog.where(message: "login").group(:account_id)
536
- ```
537
-
538
- The associated models define the inverse `belongs_to :account` association:
539
-
540
- ```rb
541
- Account::ActiveSessionKey.includes(:account).map(&:account)
542
- ```
543
-
544
- Here is an example of using associations to create a method that returns
545
- whether the account has multifactor authentication enabled:
546
-
547
- ```rb
548
- class Account < ApplicationRecord
549
- include Rodauth::Rails.model
550
-
551
- def mfa_enabled?
552
- otp_key || (sms_code && sms_code.num_failures.nil?) || recovery_codes.any?
553
- end
554
- end
555
- ```
556
-
557
- Here is another example of creating a query scope that selects accounts with
558
- multifactor authentication enabled:
559
-
560
- ```rb
561
- class Account < ApplicationRecord
562
- include Rodauth::Rails.model
563
-
564
- scope :otp_setup, -> { where(otp_key: OtpKey.all) }
565
- scope :sms_codes_setup, -> { where(sms_code: SmsCode.where(num_failures: nil)) }
566
- scope :recovery_codes_setup, -> { where(recovery_codes: RecoveryCode.all) }
567
- scope :mfa_enabled, -> { merge(otp_setup.or(sms_codes_setup).or(recovery_codes_setup)) }
568
- end
569
- ```
570
-
571
- #### Association reference
572
-
573
- Below is a list of all associations defined depending on the features loaded:
574
-
575
- | Feature | Association | Type | Model | Table (default) |
576
- | :------ | :---------- | :--- | :---- | :---- |
577
- | account_expiration | `:activity_time` | `has_one` | `ActivityTime` | `account_activity_times` |
578
- | active_sessions | `:active_session_keys` | `has_many` | `ActiveSessionKey` | `account_active_session_keys` |
579
- | audit_logging | `:authentication_audit_logs` | `has_many` | `AuthenticationAuditLog` | `account_authentication_audit_logs` |
580
- | disallow_password_reuse | `:previous_password_hashes` | `has_many` | `PreviousPasswordHash` | `account_previous_password_hashes` |
581
- | email_auth | `:email_auth_key` | `has_one` | `EmailAuthKey` | `account_email_auth_keys` |
582
- | jwt_refresh | `:jwt_refresh_keys` | `has_many` | `JwtRefreshKey` | `account_jwt_refresh_keys` |
583
- | lockout | `:lockout` | `has_one` | `Lockout` | `account_lockouts` |
584
- | lockout | `:login_failure` | `has_one` | `LoginFailure` | `account_login_failures` |
585
- | otp | `:otp_key` | `has_one` | `OtpKey` | `account_otp_keys` |
586
- | password_expiration | `:password_change_time` | `has_one` | `PasswordChangeTime` | `account_password_change_times` |
587
- | recovery_codes | `:recovery_codes` | `has_many` | `RecoveryCode` | `account_recovery_codes` |
588
- | remember | `:remember_key` | `has_one` | `RememberKey` | `account_remember_keys` |
589
- | reset_password | `:password_reset_key` | `has_one` | `PasswordResetKey` | `account_password_reset_keys` |
590
- | single_session | `:session_key` | `has_one` | `SessionKey` | `account_session_keys` |
591
- | sms_codes | `:sms_code` | `has_one` | `SmsCode` | `account_sms_codes` |
592
- | verify_account | `:verification_key` | `has_one` | `VerificationKey` | `account_verification_keys` |
593
- | verify_login_change | `:login_change_key` | `has_one` | `LoginChangeKey` | `account_login_change_keys` |
594
- | webauthn | `:webauthn_keys` | `has_many` | `WebauthnKey` | `account_webauthn_keys` |
595
- | webauthn | `:webauthn_user_id` | `has_one` | `WebauthnUserId` | `account_webauthn_user_ids` |
596
-
597
- Note that some Rodauth tables use composite primary keys, which Active Record
598
- doesn't support out of the box. For associations to work properly, you might
599
- need to add the [composite_primary_keys] gem to your Gemfile.
600
-
601
- #### Association options
602
-
603
- By default, all associations except for audit logs have `dependent: :destroy`
604
- set, to allow for easy deletion of account records in the console. You can use
605
- `:association_options` to modify global or per-association options:
606
-
607
- ```rb
608
- # don't auto-delete associations when account model is deleted
609
- Rodauth::Rails.model(association_options: { dependent: nil })
610
-
611
- # require authentication audit logs to be eager loaded before retrieval
612
- Rodauth::Rails.model(association_options: -> (name) {
613
- { strict_loading: true } if name == :authentication_audit_logs
614
- })
615
- ```
522
+ See the [rodauth-model] documentation for more details.
616
523
 
617
524
  ## Multiple configurations
618
525
 
@@ -1277,8 +1184,8 @@ conduct](https://github.com/janko/rodauth-rails/blob/master/CODE_OF_CONDUCT.md).
1277
1184
  [account_expiration]: http://rodauth.jeremyevans.net/rdoc/files/doc/account_expiration_rdoc.html
1278
1185
  [simple_ldap_authenticator]: https://github.com/jeremyevans/simple_ldap_authenticator
1279
1186
  [internal_request]: http://rodauth.jeremyevans.net/rdoc/files/doc/internal_request_rdoc.html
1280
- [composite_primary_keys]: https://github.com/composite-primary-keys/composite_primary_keys
1281
1187
  [path_class_methods]: https://rodauth.jeremyevans.net/rdoc/files/doc/path_class_methods_rdoc.html
1282
1188
  [account types]: https://github.com/janko/rodauth-rails/wiki/Account-Types
1283
1189
  [custom mailer worker]: https://github.com/janko/rodauth-rails/wiki/Custom-Mailer-Worker
1284
1190
  [Turbo]: https://turbo.hotwired.dev/
1191
+ [rodauth-model]: https://github.com/janko/rodauth-model
@@ -1,15 +1,10 @@
1
1
  require "rails/generators/base"
2
- require "rails/generators/active_record/migration"
3
- require "generators/rodauth/migration_helpers"
4
2
  require "securerandom"
5
3
 
6
4
  module Rodauth
7
5
  module Rails
8
6
  module Generators
9
7
  class InstallGenerator < ::Rails::Generators::Base
10
- include ::ActiveRecord::Generators::Migration
11
- include MigrationHelpers
12
-
13
8
  if RUBY_ENGINE == "jruby"
14
9
  SEQUEL_ADAPTERS = {
15
10
  "sqlite3" => "sqlite",
@@ -40,9 +35,7 @@ module Rodauth
40
35
  class_option :jwt, type: :boolean, desc: "Configure JWT support"
41
36
 
42
37
  def create_rodauth_migration
43
- return unless defined?(ActiveRecord::Railtie)
44
-
45
- migration_template "db/migrate/create_rodauth.rb"
38
+ invoke "rodauth:migration", migration_features, name: "create_rodauth"
46
39
  end
47
40
 
48
41
  def create_rodauth_initializer
@@ -66,8 +59,6 @@ module Rodauth
66
59
  end
67
60
 
68
61
  def create_account_model
69
- return unless defined?(ActiveRecord::Railtie)
70
-
71
62
  template "app/models/account.rb"
72
63
  end
73
64
 
@@ -112,6 +103,14 @@ module Rodauth
112
103
  scheme = "jdbc:#{scheme}" if RUBY_ENGINE == "jruby"
113
104
  scheme
114
105
  end
106
+
107
+ def activerecord_adapter
108
+ if ActiveRecord::Base.respond_to?(:connection_db_config)
109
+ ActiveRecord::Base.connection_db_config.adapter
110
+ else
111
+ ActiveRecord::Base.connection_config.fetch(:adapter)
112
+ end
113
+ end
115
114
  end
116
115
  end
117
116
  end
@@ -1,4 +1,4 @@
1
- # Used by the disallow_password_reuse feature
1
+ # Used by the disallow password reuse feature
2
2
  create_table :account_previous_password_hashes do |t|
3
3
  t.references :account, foreign_key: true<%= primary_key_type(:type) %>
4
4
  t.string :password_hash, null: false
@@ -0,0 +1,7 @@
1
+ # Used by the account expiration feature
2
+ create_table :account_activity_times do
3
+ foreign_key :id, :accounts, primary_key: true, type: :Bignum
4
+ DateTime :last_activity_at, null: false
5
+ DateTime :last_login_at, null: false
6
+ DateTime :expired_at
7
+ end
@@ -0,0 +1,8 @@
1
+ # Used by the active sessions feature
2
+ create_table :account_active_session_keys do
3
+ foreign_key :account_id, :accounts, type: :Bignum
4
+ String :session_id
5
+ Time :created_at, null: false, default: Sequel::CURRENT_TIMESTAMP
6
+ Time :last_use, null: false, default: Sequel::CURRENT_TIMESTAMP
7
+ primary_key [:account_id, :session_id]
8
+ end
@@ -0,0 +1,17 @@
1
+ # Used by the audit logging feature
2
+ create_table :account_authentication_audit_logs do
3
+ primary_key :id, type: :Bignum
4
+ foreign_key :account_id, :accounts, null: false, type: :Bignum
5
+ DateTime :at, null: false, default: Sequel::CURRENT_TIMESTAMP
6
+ String :message, null: false
7
+ <% case db.database_type -%>
8
+ <% when :postgres -%>
9
+ jsonb :metadata
10
+ <% when :sqlite, :mysql -%>
11
+ json :metadata
12
+ <% else -%>
13
+ String :metadata
14
+ <% end -%>
15
+ index [:account_id, :at], name: :audit_account_at_idx
16
+ index :at, name: :audit_at_idx
17
+ end
@@ -0,0 +1,25 @@
1
+ <% if db.database_type == :postgres -%>
2
+ enable_extension "citext"
3
+
4
+ <% end -%>
5
+ create_table :accounts do
6
+ primary_key :id, type: :Bignum
7
+ <% if db.database_type == :postgres -%>
8
+ citext :email, null: false
9
+ constraint :valid_email, email: /^[^,;@ \r\n]+@[^,@; \r\n]+\.[^,@; \r\n]+$/
10
+ <% else -%>
11
+ String :email, null: false
12
+ <% end -%>
13
+ String :status, null: false, default: "unverified"
14
+ <% if db.supports_partial_indexes? -%>
15
+ index :email, unique: true, where: { status: ["unverified", "verified"] }
16
+ <% else -%>
17
+ index :email, unique: true
18
+ <% end -%>
19
+ end
20
+
21
+ # Used if storing password hashes in a separate table (default)
22
+ create_table :account_password_hashes do
23
+ foreign_key :id, :accounts, primary_key: true, type: :Bignum
24
+ String :password_hash, null: false
25
+ end
@@ -0,0 +1,6 @@
1
+ # Used by the disallow password reuse feature
2
+ create_table :account_previous_password_hashes do
3
+ primary_key :id, type: :Bignum
4
+ foreign_key :account_id, :accounts, type: :Bignum
5
+ String :password_hash, null: false
6
+ end
@@ -0,0 +1,7 @@
1
+ # Used by the email auth feature
2
+ create_table :account_email_auth_keys do
3
+ foreign_key :id, :accounts, primary_key: true, type: :Bignum
4
+ String :key, null: false
5
+ DateTime :deadline, null: false
6
+ DateTime :email_last_sent, null: false, default: Sequel::CURRENT_TIMESTAMP
7
+ end
@@ -0,0 +1,8 @@
1
+ # Used by the jwt refresh feature
2
+ create_table :account_jwt_refresh_keys do
3
+ primary_key :id, type: :Bignum
4
+ foreign_key :account_id, :accounts, null: false, type: :Bignum
5
+ String :key, null: false
6
+ DateTime :deadline, null: false
7
+ index :account_id, name: :account_jwt_rk_account_id_idx
8
+ end
@@ -0,0 +1,11 @@
1
+ # Used by the lockout feature
2
+ create_table :account_login_failures do
3
+ foreign_key :id, :accounts, primary_key: true, type: :Bignum
4
+ Integer :number, null: false, default: 1
5
+ end
6
+ create_table :account_lockouts do
7
+ foreign_key :id, :accounts, primary_key: true, type: :Bignum
8
+ String :key, null: false
9
+ DateTime :deadline, null: false
10
+ DateTime :email_last_sent
11
+ end
@@ -0,0 +1,7 @@
1
+ # Used by the otp feature
2
+ create_table :account_otp_keys do
3
+ foreign_key :id, :accounts, primary_key: true, type: :Bignum
4
+ String :key, null: false
5
+ Integer :num_failures, null: false, default: 0
6
+ Time :last_use, null: false, default: Sequel::CURRENT_TIMESTAMP
7
+ end
@@ -0,0 +1,5 @@
1
+ # Used by the password expiration feature
2
+ create_table :account_password_change_times do
3
+ foreign_key :id, :accounts, primary_key: true, type: :Bignum
4
+ DateTime :changed_at, null: false, default: Sequel::CURRENT_TIMESTAMP
5
+ end
@@ -0,0 +1,6 @@
1
+ # Used by the recovery codes feature
2
+ create_table :account_recovery_codes do
3
+ foreign_key :id, :accounts, type: :Bignum
4
+ String :code
5
+ primary_key [:id, :code]
6
+ end
@@ -0,0 +1,6 @@
1
+ # Used by the remember me feature
2
+ create_table :account_remember_keys do
3
+ foreign_key :id, :accounts, primary_key: true, type: :Bignum
4
+ String :key, null: false
5
+ DateTime :deadline, null: false
6
+ end
@@ -0,0 +1,7 @@
1
+ # Used by the password reset feature
2
+ create_table :account_password_reset_keys do
3
+ foreign_key :id, :accounts, primary_key: true, type: :Bignum
4
+ String :key, null: false
5
+ DateTime :deadline, null: false
6
+ DateTime :email_last_sent, null: false, default: Sequel::CURRENT_TIMESTAMP
7
+ end
@@ -0,0 +1,5 @@
1
+ # Used by the single session feature
2
+ create_table :account_session_keys do
3
+ foreign_key :id, :accounts, primary_key: true, type: :Bignum
4
+ String :key, null: false
5
+ end
@@ -0,0 +1,8 @@
1
+ # Used by the sms codes feature
2
+ create_table :account_sms_codes do
3
+ foreign_key :id, :accounts, primary_key: true, type: :Bignum
4
+ String :phone_number, null: false
5
+ Integer :num_failures
6
+ String :code
7
+ DateTime :code_issued_at, null: false, default: Sequel::CURRENT_TIMESTAMP
8
+ end
@@ -0,0 +1,7 @@
1
+ # Used by the account verification feature
2
+ create_table :account_verification_keys do
3
+ foreign_key :id, :accounts, primary_key: true, type: :Bignum
4
+ String :key, null: false
5
+ DateTime :requested_at, null: false, default: Sequel::CURRENT_TIMESTAMP
6
+ DateTime :email_last_sent, null: false, default: Sequel::CURRENT_TIMESTAMP
7
+ end
@@ -0,0 +1,7 @@
1
+ # Used by the verify login change feature
2
+ create_table :account_login_change_keys do
3
+ foreign_key :id, :accounts, primary_key: true, type: :Bignum
4
+ String :key, null: false
5
+ String :login, null: false
6
+ DateTime :deadline, null: false
7
+ end
@@ -0,0 +1,13 @@
1
+ # Used by the webauthn feature
2
+ create_table :account_webauthn_user_ids do
3
+ foreign_key :id, :accounts, primary_key: true, type: :Bignum
4
+ String :webauthn_id, null: false
5
+ end
6
+ create_table :account_webauthn_keys do
7
+ foreign_key :account_id, :accounts, type: :Bignum
8
+ String :webauthn_id
9
+ String :public_key, null: false
10
+ Integer :sign_count, null: false
11
+ Time :last_use, null: false, default: Sequel::CURRENT_TIMESTAMP
12
+ primary_key [:account_id, :webauthn_id]
13
+ end
@@ -1,14 +1,11 @@
1
1
  require "rails/generators/base"
2
2
  require "rails/generators/active_record/migration"
3
- require "generators/rodauth/migration_helpers"
3
+ require "erb"
4
4
 
5
5
  module Rodauth
6
6
  module Rails
7
7
  module Generators
8
8
  class MigrationGenerator < ::Rails::Generators::Base
9
- include ::ActiveRecord::Generators::Migration
10
- include MigrationHelpers
11
-
12
9
  source_root "#{__dir__}/templates"
13
10
  namespace "rodauth:migration"
14
11
 
@@ -20,19 +17,102 @@ module Rodauth
20
17
  desc: "Name of the generated migration file"
21
18
 
22
19
  def create_rodauth_migration
23
- return unless defined?(ActiveRecord::Railtie)
24
20
  return if features.empty?
25
21
 
26
- migration_template "db/migrate/create_rodauth.rb", "#{migration_name}.rb"
22
+ migration_template "db/migrate/create_rodauth.rb", File.join(db_migrate_path, "#{migration_name}.rb")
27
23
  end
28
24
 
29
- def migration_features
30
- features
31
- end
25
+ private
32
26
 
33
27
  def migration_name
34
28
  options[:name] || "create_rodauth_#{features.join("_")}"
35
29
  end
30
+
31
+ def migration_content
32
+ features
33
+ .select { |feature| File.exist?(migration_chunk(feature)) }
34
+ .map { |feature| File.read(migration_chunk(feature)) }
35
+ .map { |content| erb_eval(content) }
36
+ .join("\n")
37
+ .indent(4)
38
+ end
39
+
40
+ def erb_eval(content)
41
+ if ERB.version[/\d+\.\d+\.\d+/].to_s >= "2.2.0"
42
+ ERB.new(content, trim_mode: "-").result(binding)
43
+ else
44
+ ERB.new(content, 0, "-").result(binding)
45
+ end
46
+ end
47
+
48
+ if defined?(::ActiveRecord::Railtie) # Active Record
49
+ include ::ActiveRecord::Generators::Migration
50
+
51
+ def db_migrate_path
52
+ return "db/migrate" unless ActiveRecord.version >= Gem::Version.new("5.0")
53
+
54
+ super
55
+ end
56
+
57
+ def migration_chunk(feature)
58
+ "#{__dir__}/migration/active_record/#{feature}.erb"
59
+ end
60
+
61
+ def migration_version
62
+ return unless ActiveRecord.version >= Gem::Version.new("5.0")
63
+
64
+ "[#{ActiveRecord::Migration.current_version}]"
65
+ end
66
+
67
+ def activerecord_adapter
68
+ if ActiveRecord::Base.respond_to?(:connection_db_config)
69
+ ActiveRecord::Base.connection_db_config.adapter
70
+ else
71
+ ActiveRecord::Base.connection_config.fetch(:adapter)
72
+ end
73
+ end
74
+
75
+ def primary_key_type(key = :id)
76
+ generators = ::Rails.application.config.generators
77
+ column_type = generators.options[:active_record][:primary_key_type]
78
+
79
+ return unless column_type
80
+
81
+ if key
82
+ ", #{key}: :#{column_type}"
83
+ else
84
+ column_type
85
+ end
86
+ end
87
+
88
+ def current_timestamp
89
+ if ActiveRecord.version >= Gem::Version.new("5.0")
90
+ %(-> { "CURRENT_TIMESTAMP" })
91
+ else
92
+ %(OpenStruct.new(quoted_id: "CURRENT_TIMESTAMP"))
93
+ end
94
+ end
95
+ else # Sequel
96
+ include ::Rails::Generators::Migration
97
+
98
+ def self.next_migration_number(dirname)
99
+ next_migration_number = current_migration_number(dirname) + 1
100
+ [Time.now.utc.strftime('%Y%m%d%H%M%S'), format('%.14d', next_migration_number)].max
101
+ end
102
+
103
+ def db_migrate_path
104
+ "db/migrate"
105
+ end
106
+
107
+ def migration_chunk(feature)
108
+ "#{__dir__}/migration/sequel/#{feature}.erb"
109
+ end
110
+
111
+ def db
112
+ db = ::Sequel::DATABASES.first if defined?(::Sequel)
113
+ db or fail Rodauth::Rails::Error, "missing Sequel database connection"
114
+ end
115
+ end
36
116
  end
37
117
  end
38
118
  end