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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +26 -0
- data/README.md +10 -103
- data/lib/generators/rodauth/install_generator.rb +9 -10
- data/lib/generators/rodauth/migration/{account_expiration.erb → active_record/account_expiration.erb} +0 -0
- data/lib/generators/rodauth/migration/{active_sessions.erb → active_record/active_sessions.erb} +0 -0
- data/lib/generators/rodauth/migration/{audit_logging.erb → active_record/audit_logging.erb} +0 -0
- data/lib/generators/rodauth/migration/{base.erb → active_record/base.erb} +0 -0
- data/lib/generators/rodauth/migration/{disallow_password_reuse.erb → active_record/disallow_password_reuse.erb} +1 -1
- data/lib/generators/rodauth/migration/{email_auth.erb → active_record/email_auth.erb} +0 -0
- data/lib/generators/rodauth/migration/{jwt_refresh.erb → active_record/jwt_refresh.erb} +0 -0
- data/lib/generators/rodauth/migration/{lockout.erb → active_record/lockout.erb} +0 -0
- data/lib/generators/rodauth/migration/{otp.erb → active_record/otp.erb} +0 -0
- data/lib/generators/rodauth/migration/{password_expiration.erb → active_record/password_expiration.erb} +0 -0
- data/lib/generators/rodauth/migration/{recovery_codes.erb → active_record/recovery_codes.erb} +0 -0
- data/lib/generators/rodauth/migration/{remember.erb → active_record/remember.erb} +0 -0
- data/lib/generators/rodauth/migration/{reset_password.erb → active_record/reset_password.erb} +0 -0
- data/lib/generators/rodauth/migration/{single_session.erb → active_record/single_session.erb} +0 -0
- data/lib/generators/rodauth/migration/{sms_codes.erb → active_record/sms_codes.erb} +0 -0
- data/lib/generators/rodauth/migration/{verify_account.erb → active_record/verify_account.erb} +0 -0
- data/lib/generators/rodauth/migration/{verify_login_change.erb → active_record/verify_login_change.erb} +0 -0
- data/lib/generators/rodauth/migration/{webauthn.erb → active_record/webauthn.erb} +0 -0
- data/lib/generators/rodauth/migration/sequel/account_expiration.erb +7 -0
- data/lib/generators/rodauth/migration/sequel/active_sessions.erb +8 -0
- data/lib/generators/rodauth/migration/sequel/audit_logging.erb +17 -0
- data/lib/generators/rodauth/migration/sequel/base.erb +25 -0
- data/lib/generators/rodauth/migration/sequel/disallow_password_reuse.erb +6 -0
- data/lib/generators/rodauth/migration/sequel/email_auth.erb +7 -0
- data/lib/generators/rodauth/migration/sequel/jwt_refresh.erb +8 -0
- data/lib/generators/rodauth/migration/sequel/lockout.erb +11 -0
- data/lib/generators/rodauth/migration/sequel/otp.erb +7 -0
- data/lib/generators/rodauth/migration/sequel/password_expiration.erb +5 -0
- data/lib/generators/rodauth/migration/sequel/recovery_codes.erb +6 -0
- data/lib/generators/rodauth/migration/sequel/remember.erb +6 -0
- data/lib/generators/rodauth/migration/sequel/reset_password.erb +7 -0
- data/lib/generators/rodauth/migration/sequel/single_session.erb +5 -0
- data/lib/generators/rodauth/migration/sequel/sms_codes.erb +8 -0
- data/lib/generators/rodauth/migration/sequel/verify_account.erb +7 -0
- data/lib/generators/rodauth/migration/sequel/verify_login_change.erb +7 -0
- data/lib/generators/rodauth/migration/sequel/webauthn.erb +13 -0
- data/lib/generators/rodauth/migration_generator.rb +89 -9
- data/lib/generators/rodauth/templates/app/mailers/rodauth_mailer.rb +38 -28
- data/lib/generators/rodauth/templates/app/misc/rodauth_main.rb +6 -6
- data/lib/generators/rodauth/templates/app/models/account.rb +8 -0
- data/lib/generators/rodauth/templates/app/views/rodauth_mailer/verify_login_change.text.erb +2 -2
- data/lib/generators/rodauth/templates/db/migrate/create_rodauth.rb +8 -0
- data/lib/rodauth/rails/middleware.rb +9 -0
- data/lib/rodauth/rails/model.rb +2 -97
- data/lib/rodauth/rails/version.rb +1 -1
- data/lib/rodauth/rails.rb +2 -1
- data/rodauth-rails.gemspec +1 -0
- metadata +52 -22
- data/lib/generators/rodauth/migration_helpers.rb +0 -77
- data/lib/rodauth/rails/model/associations.rb +0 -195
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 9a42fbc8522feb0a7d18227ca1cb3d79448c6d5d824befb040699afdb69318ea
|
4
|
+
data.tar.gz: 4d60492cc1ea2acf8176ffb6a05c6f0aba7deda63d1541da42de7f558e55b6e6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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::
|
488
|
-
defines a password attribute and associations for
|
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 <
|
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
|
-
|
498
|
-
|
499
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
File without changes
|
data/lib/generators/rodauth/migration/{active_sessions.erb → active_record/active_sessions.erb}
RENAMED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
data/lib/generators/rodauth/migration/{recovery_codes.erb → active_record/recovery_codes.erb}
RENAMED
File without changes
|
File without changes
|
data/lib/generators/rodauth/migration/{reset_password.erb → active_record/reset_password.erb}
RENAMED
File without changes
|
data/lib/generators/rodauth/migration/{single_session.erb → active_record/single_session.erb}
RENAMED
File without changes
|
File without changes
|
data/lib/generators/rodauth/migration/{verify_account.erb → active_record/verify_account.erb}
RENAMED
File without changes
|
File without changes
|
File without changes
|
@@ -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,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,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,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,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 "
|
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
|
-
|
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
|