rodauth-rails 1.2.2 → 1.4.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 +4 -4
- data/CHANGELOG.md +22 -0
- data/README.md +108 -2
- 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} +2 -7
- 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 +24 -0
- data/lib/generators/rodauth/templates/app/misc/rodauth_main.rb +4 -1
- data/lib/generators/rodauth/templates/app/models/account.rb +11 -0
- data/lib/generators/rodauth/templates/db/migrate/create_rodauth.rb +8 -0
- data/lib/rodauth/rails/app.rb +19 -7
- data/lib/rodauth/rails/controller_methods.rb +9 -0
- data/lib/rodauth/rails/feature/associations.rb +54 -0
- data/lib/rodauth/rails/feature/base.rb +10 -0
- data/lib/rodauth/rails/feature/instrumentation.rb +8 -0
- data/lib/rodauth/rails/feature.rb +2 -0
- data/lib/rodauth/rails/middleware.rb +9 -0
- data/lib/rodauth/rails/model.rb +8 -8
- data/lib/rodauth/rails/railtie.rb +9 -0
- data/lib/rodauth/rails/test/controller.rb +41 -0
- data/lib/rodauth/rails/test.rb +7 -0
- data/lib/rodauth/rails/version.rb +1 -1
- data/rodauth-rails.gemspec +2 -1
- metadata +57 -26
- data/lib/generators/rodauth/migration_helpers.rb +0 -77
- data/lib/rodauth/rails/app/flash.rb +0 -45
- data/lib/rodauth/rails/app/middleware.rb +0 -36
- 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: 8b5fb22e3d8c84eafedffa3463b4e0ecdf481189b8b48adc2d31005f86251d87
|
4
|
+
data.tar.gz: 142e229b7c9a9b078f773f99885117268e759df2eb49e5252ce21754dcf61763
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b1c30c5b1714a80466ad3775720be50d2f02b8d06d016638e6d1ebfb7515cd6060463b975f9810977fe74eb7330ce12b9c6ff81839e41b2e4044615c606ca7bb
|
7
|
+
data.tar.gz: 4a532058b27cfc07d2ed234457b880a609c7b35186db976a547be2e83d1c18df7ac72402a9d52155819f2d97edb63b0e7742e908b6cb3ed1b40c2deae18e7e2e
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,25 @@
|
|
1
|
+
## 1.4.0 (2022-05-04)
|
2
|
+
|
3
|
+
* Move association definitions to `#associations` Rodauth method, allowing external features to extend them (@janko)
|
4
|
+
|
5
|
+
* Add Sequel support for generating database migrations, model, and mailer (@janko)
|
6
|
+
|
7
|
+
* Skip calling Rodauth app on asset requests when using Sprockets or Propshaft (@janko)
|
8
|
+
|
9
|
+
## 1.3.1 (2022-04-22)
|
10
|
+
|
11
|
+
* Ensure response status is logged when calling a halting rodauth method inside a controller (@janko)
|
12
|
+
|
13
|
+
## 1.3.0 (2022-04-01)
|
14
|
+
|
15
|
+
* Store password hash on the `accounts` table in generated Rodauth migration and configuration (@janko)
|
16
|
+
|
17
|
+
* Add support for controller testing with Minitest or RSpec (@janko)
|
18
|
+
|
19
|
+
* Fix `enum` declaration in generated `Account` model for Active Record < 7.0 (@janko)
|
20
|
+
|
21
|
+
* Ensure `require_login_redirect` points to the login page even if the login route changes (@janko)
|
22
|
+
|
1
23
|
## 1.2.2 (2022-02-22)
|
2
24
|
|
3
25
|
* Fix flash messages not being preserved through consecutive redirects (@janko)
|
data/README.md
CHANGED
@@ -10,7 +10,6 @@ Provides Rails integration for the [Rodauth] authentication framework.
|
|
10
10
|
* [Rails demo](https://github.com/janko/rodauth-demo-rails)
|
11
11
|
* [JSON API guide](https://github.com/janko/rodauth-rails/wiki/JSON-API)
|
12
12
|
* [OmniAuth guide](https://github.com/janko/rodauth-rails/wiki/OmniAuth)
|
13
|
-
* [Testing guide](https://github.com/janko/rodauth-rails/wiki/Testing)
|
14
13
|
|
15
14
|
🎥 Screencasts:
|
16
15
|
|
@@ -40,7 +39,7 @@ of the advantages that stand out for me:
|
|
40
39
|
* consistent before/after hooks around everything
|
41
40
|
* dedicated object encapsulating all authentication logic
|
42
41
|
|
43
|
-
One
|
42
|
+
One common concern is the fact that, unlike most other authentication
|
44
43
|
frameworks for Rails, Rodauth uses [Sequel] for database interaction instead of
|
45
44
|
Active Record. There are good reasons for this, and to make Rodauth work
|
46
45
|
smoothly alongside Active Record, rodauth-rails configures Sequel to [reuse
|
@@ -615,6 +614,33 @@ Rodauth::Rails.model(association_options: -> (name) {
|
|
615
614
|
})
|
616
615
|
```
|
617
616
|
|
617
|
+
#### Extending Associations
|
618
|
+
|
619
|
+
External features can extend the list of associations with their own
|
620
|
+
definitions, which the model mixin will pick up and declare the new associations
|
621
|
+
on the model.
|
622
|
+
|
623
|
+
```rb
|
624
|
+
# lib/rodauth/features/foo.rb
|
625
|
+
module Rodauth
|
626
|
+
Feature.define(:foo, :Foo) do
|
627
|
+
auth_value_method :foo_table, :account_foos
|
628
|
+
auth_value_method :foo_id_column, :id
|
629
|
+
|
630
|
+
def associations
|
631
|
+
list = super
|
632
|
+
list << {
|
633
|
+
name: :foo, # will define `Account::Foo` model
|
634
|
+
type: :one, # or :many
|
635
|
+
table: foo_table,
|
636
|
+
foreign_key: foo_id_column
|
637
|
+
}
|
638
|
+
list
|
639
|
+
end
|
640
|
+
end
|
641
|
+
end
|
642
|
+
```
|
643
|
+
|
618
644
|
## Multiple configurations
|
619
645
|
|
620
646
|
If you need to handle multiple types of accounts that require different
|
@@ -783,6 +809,86 @@ Rodauth::Rails.rodauth(session: { two_factor_auth_setup: true })
|
|
783
809
|
Rodauth::Rails.rodauth(:admin, params: { "param" => "value" })
|
784
810
|
```
|
785
811
|
|
812
|
+
## Testing
|
813
|
+
|
814
|
+
For system and integration tests, which run the whole middleware stack,
|
815
|
+
authentication can be exercised normally via HTTP endpoints. See [this wiki
|
816
|
+
page](https://github.com/janko/rodauth-rails/wiki/Testing) for some examples.
|
817
|
+
|
818
|
+
For controller tests, you can log in accounts by modifying the session:
|
819
|
+
|
820
|
+
```rb
|
821
|
+
# app/controllers/articles_controller.rb
|
822
|
+
class ArticlesController < ApplicationController
|
823
|
+
before_action -> { rodauth.require_authentication }
|
824
|
+
|
825
|
+
def index
|
826
|
+
# ...
|
827
|
+
end
|
828
|
+
end
|
829
|
+
```
|
830
|
+
```rb
|
831
|
+
# test/controllers/articles_controller_test.rb
|
832
|
+
class ArticlesControllerTest < ActionController::TestCase
|
833
|
+
test "required authentication" do
|
834
|
+
get :index
|
835
|
+
|
836
|
+
assert_response 302
|
837
|
+
assert_redirected_to "/login"
|
838
|
+
assert_equal "Please login to continue", flash[:alert]
|
839
|
+
|
840
|
+
account = Account.create!(email: "user@example.com", password: "secret", status: "verified")
|
841
|
+
login(account)
|
842
|
+
|
843
|
+
get :index
|
844
|
+
assert_response 200
|
845
|
+
|
846
|
+
logout
|
847
|
+
|
848
|
+
get :index
|
849
|
+
assert_response 302
|
850
|
+
assert_equal "Please login to continue", flash[:alert]
|
851
|
+
end
|
852
|
+
|
853
|
+
private
|
854
|
+
|
855
|
+
# Manually modify the session into what Rodauth expects.
|
856
|
+
def login(account)
|
857
|
+
session[:account_id] = account.id
|
858
|
+
session[:authenticated_by] = ["password"] # or ["password", "totp"] for MFA
|
859
|
+
end
|
860
|
+
|
861
|
+
def logout
|
862
|
+
session.clear
|
863
|
+
end
|
864
|
+
end
|
865
|
+
```
|
866
|
+
|
867
|
+
If you're using multiple configurations with different session prefixes, you'll need
|
868
|
+
to make sure to use those in controller tests as well:
|
869
|
+
|
870
|
+
```rb
|
871
|
+
class RodauthAdmin < Rodauth::Rails::Auth
|
872
|
+
configure do
|
873
|
+
session_key_prefix "admin_"
|
874
|
+
end
|
875
|
+
end
|
876
|
+
```
|
877
|
+
```rb
|
878
|
+
# in a controller test:
|
879
|
+
session[:admin_account_id] = account.id
|
880
|
+
session[:admin_authenticated_by] = ["password"]
|
881
|
+
```
|
882
|
+
|
883
|
+
If you want to access the Rodauth instance in controller tests, you can do so
|
884
|
+
through the controller instance:
|
885
|
+
|
886
|
+
```rb
|
887
|
+
# in a controller test:
|
888
|
+
@controller.rodauth #=> #<RodauthMain ...>
|
889
|
+
@controller.rodauth(:admin) #=> #<RodauthAdmin ...>
|
890
|
+
```
|
891
|
+
|
786
892
|
## Configuring
|
787
893
|
|
788
894
|
### Configuration methods
|
@@ -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
|
@@ -3,23 +3,18 @@ enable_extension "citext"
|
|
3
3
|
|
4
4
|
<% end -%>
|
5
5
|
create_table :accounts<%= primary_key_type %> do |t|
|
6
|
+
t.integer :status, null: false, default: 1
|
6
7
|
<% case activerecord_adapter -%>
|
7
8
|
<% when "postgresql" -%>
|
8
9
|
t.citext :email, null: false
|
9
10
|
<% else -%>
|
10
11
|
t.string :email, null: false
|
11
12
|
<% end -%>
|
12
|
-
t.integer :status, null: false, default: 1
|
13
13
|
<% case activerecord_adapter -%>
|
14
14
|
<% when "postgresql", "sqlite3" -%>
|
15
15
|
t.index :email, unique: true, where: "status IN (1, 2)"
|
16
16
|
<% else -%>
|
17
17
|
t.index :email, unique: true
|
18
18
|
<% end -%>
|
19
|
-
|
20
|
-
|
21
|
-
# Used if storing password hashes in a separate table (default)
|
22
|
-
create_table :account_password_hashes<%= primary_key_type %> do |t|
|
23
|
-
t.foreign_key :accounts, column: :id
|
24
|
-
t.string :password_hash, null: false
|
19
|
+
t.string :password_hash
|
25
20
|
end
|
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
|